Does argparse (python) support mutually exclusive groups of arguments?

混江龙づ霸主 提交于 2019-11-27 02:40:03

问题


If I have the arguments '-a', '-b', '-c', '-d', with the add_mutually_exclusive_group() function my program will have to use just one of them. Is there a way to combine that, so that the program will accept only either '-a 999 -b 999' or '-c 999 -d 999'?

Edit: adding a simple program for more clarity:

>>> parser = argparse.ArgumentParser()
>>> group = parser.add_mutually_exclusive_group()
>>> group.add_argument('-a')
>>> group.add_argument('-b')
>>> group.add_argument('-c')
>>> group.add_argument('-d')

Then only ./app.py -a | ./app.py -b | ./app.py -c | ./app.py -d can be called. Is it possible to have argparse group the exclusion groups, so that only ./app.py -a .. -b .. | ./app.py -c .. -d .. be called?


回答1:


EDIT: Never mind. Because argparse makes the horrible choice of having to create an option when invoking group.add_argument. That wouldn't be my design choice. If you're desperate for this feature, you can try doing it with ConflictsOptionParser:

# exclusivegroups.py
import conflictsparse

parser = conflictsparse.ConflictsOptionParser()
a_opt = parser.add_option('-a')
b_opt = parser.add_option('-b')
c_opt = parser.add_option('-c')
d_opt = parser.add_option('-d')

import itertools
compatible_opts1 = (a_opt, b_opt)
compatible_opts2 = (c_opt, d_opt)
exclusives = itertools.product(compatible_opts1, compatible_opts2)
for exclusive_grp in exclusives:
    parser.register_conflict(exclusive_grp)


opts, args = parser.parse_args()
print "opts: ", opts
print "args: ", args

Thus when we invoke it, we can see we get the desired effect.

$ python exclusivegroups.py -a 1 -b 2
opts:  {'a': '1', 'c': None, 'b': '2', 'd': None}
args:  []
$ python exclusivegroups.py -c 3 -d 2
opts:  {'a': None, 'c': '3', 'b': None, 'd': '2'}
args:  []
$ python exclusivegroups.py -a 1 -b 2 -c 3
Usage: exclusivegroups.py [options]

exclusivegroups.py: error: -b, -c are incompatible options.

The warning message doesn't inform you that both '-a' and '-b' are incompatible with '-c', however a more appropriate error message could be crafted. Older, wrong answer below.

OLDER EDIT: [This edit is wrong, although wouldn't it be just a perfect world if argparse worked this way?] My previous answer actually was incorrect, you should be able to do this with argparse by specifying one group per mutually exclusive options. We can even use itertools to generalize the process. And make it so we don't have to type out all the combinations explicitly:

import itertools
compatible_opts1 = ('-a', '-b')
compatible_opts2 = ('-c', '-d')
exclusives = itertools.product(compatible_opts1, compatible_opts2)
for exclusive_grp in exclusives:
    group = parser.add_mutually_exclusive_group()
    group.add_argument(exclusive_grp[0])
    group.add_argument(exclusive_grp[1])



回答2:


Just stumbled on this problem myself. From my reading of the argparse docs, there doesn't seem to be a simple way to achieve that within argparse. I considered using parse_known_args, but that soon amounts to writing a special-purpose version of argparse ;-)

Perhaps a bug report is in order. In the meanwhile, if you're willing to make your user do a tiny bit extra typing, you can fake it with subgroups (like how git and svn's arguments work), e.g.

    subparsers = parser.add_subparsers()
    p_ab = subparsers.add_parser('ab')
    p_ab.add_argument(...)

    p_cd = subparsers.add_parser('cd')
    p_cd.add_argument(...)

Not ideal, but at least it gives you the good from argparse without too much ugly hackery. I ended up doing away with the switches and just using the subparser operations with required subarguments.




回答3:


Subparsers?

Similar to unhammer's answer, but with more user control. Note: I have not actually tested this method, but it should work in theory and with the capabilities of python.

You can create two parsers, one for each of the two groups, and use conditionals to do the mutually exclusive part. Essentially using argparse for only part of the argument parsing. Using this method, you can go beyond the limitations of unhammer's answer as well.

# Python 3
import argparse

try:
    parser = argparse.ArgumentParser()
    parser.add_argument('-a')
    parser.add_argument('-b')
    args = parser.parse_args
except argparse.ArgumentError:
    parser = argparse.ArgumentParser()
    parser.add_argument('-c')
    parser.add_argument('-d')
    args = parser.parse_args


来源:https://stackoverflow.com/questions/4770576/does-argparse-python-support-mutually-exclusive-groups-of-arguments

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