Convert many argparse conditional arguements to a more pythonic solution

為{幸葍}努か 提交于 2019-12-13 04:26:28

问题


I am working on a cli application using python. I have a set of arguments that are mutually exclusive and a set of arguments that must be there if one of those mutually exclusive arguments is passed.
I have got it working using brute force and lengthy if conditions, but I feel like there is a neat way to do this. Researching about that told me subparsers might be useful, however I am not able to correctly use subparsers at all.
The conditions I want are the following-
Main activities
+-----+--------+--------+--------+---------+
| get | create | delete | update | remove |
+-----+--------+--------+--------+---------+

These main activities are mutually exclusive.

If get is specified, then there should not be any more arguements.
If delete is specified then there should not be any more arguements.
If remove is specified then -r is mandatory.
If update is specified then -f mandatory, and-cd is optional.
If create is specified, then -d is mandatory, -m and -f are optional where -m and -f are mutually exclusive.

The brute force code is as follows -

import argparse
parser = argparse.ArgumentParser(description='Check args')
#get
parser.add_argument('-g', '--get', help='')
#create
parser.add_argument('-c', '--create', help='')
parser.add_argument('-d', '--display', help='')
parser.add_argument('-m', '--message', help='')
parser.add_argument('-f', '--file', help='')
#update
parser.add_argument('-u', '--update', help='')
parser.add_argument('-cd', '--changed', help='')
#delete
parser.add_argument('-del', '--delete', help='')
#remove
parser.add_argument('-rm', help='')
parser.add_argument('-r', '--remove', help='')

args = vars(parser.parse_args())

if args['get'] is not None:
    if args['create'] is None and args['display'] is None and args['message'] is None and args['file'] is None and args['update'] is None and args['changed'] is None and args['delete'] is None and args['rm'] is None and args['remove'] is None:
        print(args['get'])
    else:
        print('Dont mix get with others')
        exit()

if args['create']:
    if args['get'] is None and args['message'] is None and args['file'] is None and args['update'] is None and args['changed'] is None and args['delete'] is None and args['rm'] is None and args['remove'] is None:
        print(args['create'])
    else:
        print('Dont mix create with others')
        exit()
    if args['display'] is None:
        print('Missing display')

if args['update']:
    if args['get'] is None and args['create'] is None and args['display'] is None and args['message'] is None and args['delete'] is None and args['rm'] is None and args['remove'] is None:
        print(args['update'])
    else:
        print('Dont mix update with others')
        exit()
    if args['file'] is None:
        print('Missing file')

if args['delete']:
    if args['get'] is None and args['create'] is None and args['display'] is None and args['message'] is None and args['file'] is None and args['update'] is None and args['changed'] is None and args['rm'] is None and args['remove'] is None:
        print(args['delete'])
    else:
        print('Dont mix delete with others')
        exit()

if args['rm']:
    if args['get'] is None and args['create'] is None and args['display'] is None and args['message'] is None and args['file'] is None and args['update'] is None and args['changed'] is None and args['delete'] is None:
        print(args['rm'])
    else:
        print('Dont mix resource management with others')
        exit()
    if args['remove'] is None:
        print('Missing remove')

Is there any way to make it more pythonic ?

EDIT 1:
Why have I not included code using subparser ?
Because so far I have understood, subparsers don't themselves take any values. So in my case I want to be able to execute the script like this -

  • prog -g xxyy
  • prog -c xxyy -d 'Hello World'
  • prog -u xxyy -f 'file.md' -cd 'Foo bar baz'

Where as using subparsers, they would become something like (which I don't want, correct me if I am wrong)-

  • prog get -g xxyy
  • prog create -c xxyy -d 'Hello World'

EDIT 2
I figured out how to have mutually exclusive arguements using add_mutually_exclusive_group

parser = argparse.ArgumentParser(description='Mutex')
group = parser.add_mutually_exclusive_group()
group.add_argument('-g')
group.add_argument('-c')
group.add_argument('-u')
group.add_argument('-del')
group.add_argument('-rm')

EDIT 3
I am not able to get the subparse work. The following code (working with a subset) throws the error error: invalid choice: 'world' (choose from '-m', '-f') if the command is $ python3 parse2.py -c hello -m world

parser = argparse.ArgumentParser(description='Mutex')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-g')
group.add_argument('-c')
subparser = parser.add_subparsers()
parser_a = subparser.add_parser('-m')
parser_b = subparser.add_parser('-f')
args = parser.parse_args()
print(args)

EDIT 4
I have almost solved my issue with the following -

import argparse
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='main') # , required=True) in Py3.7
sp.required = True    # in py 3.6
p1 = sp.add_parser('get')
p1.add_argument('id')

p2 = sp.add_parser('update')
p2.add_argument('id')
p2.add_argument('-f', required=True)
p2.add_argument('-cd')

p3 = sp.add_parser('create')
p3.add_argument('name')
p3.add_argument('-d', required=True)
p3_mutex = p3.add_mutually_exclusive_group()
p3_mutex.add_argument('-f')
p3_mutex.add_argument('-m')

p4 = sp.add_parser('delete')
p4.add_argument('id')

p5 = sp.add_parser('remove')
p5.add_argument('id')
p5.add_argument('-r', required=True)

args = parser.parse_args()
print(args)

However I want to know if it is possible to add the add_mutually_exclusive_group for get, delete, create, update and remove or do I have to do that using if conditions?


回答1:


A start of a subparser version:

import argparse
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='main') # , required=True) in Py3.7
sp.required = True    # in py 3.6
p1 = sp.add_parser('get')
p1.add_argument('xxyy')

p2 = sp.add_parser('update')
p2.add_argument('xxyy')
p2.add_argument('-r', required=True)
p2.add_argument('--cd')
# and so forth
args = parser.parse_args()
print(args)

sample runs

0914:~/mypy$ python3 stack53307678.py -h
usage: stack53307678.py [-h] {get,update} ...

positional arguments:
  {get,update}

optional arguments:
  -h, --help    show this help message and exit
0914:~/mypy$ python3 stack53307678.py get abc
Namespace(main='get', xxyy='abc')
0914:~/mypy$ python3 stack53307678.py update abc -r foo --cd x
Namespace(cd='x', main='update', r='foo', xxyy='abc')


来源:https://stackoverflow.com/questions/53307678/convert-many-argparse-conditional-arguements-to-a-more-pythonic-solution

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!