问题
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