How to define a mutually exclusive group of two positional arguments?
Solution 1
The question is a year old, but since all the answers suggest a different syntax, I'll give something closer to the OP.
First, the problems with the OP code:
A positional store_true
does not make sense (even if it is allowed). It requires no arguments, so it is always True
. Giving an 'all' will produce error: unrecognized arguments: all
.
The other argument takes one value and assigns it to the name
attribute. It does not accept an additional process
value.
Regarding the mutually_exclusive_group
. That error message is raised even before parse_args
. For such a group to make sense, all the alternatives have to be optional. That means either having a --
flag, or be a postional with nargs
equal to ?
or *
. And doesn't make sense to have more than one such positional in the group.
The simplest alternative to using --all
and --name
, would be something like this:
p=argparse.ArgumentParser()
p.add_argument('mode', choices=['all','name'])
p.add_argument('process',nargs='?')
def foo(args):
if args.mode == 'all' and args.process:
pass # can ignore the process value or raise a error
if args.mode == 'name' and args.process is None:
p.error('name mode requires a process')
args = p.parse_args()
foo(args) # now test the namespace for correct `process` argument.
Accepted namespaces would look like:
Namespace(mode='name', process='process1')
Namespace(mode='all', process=None)
choices
imitates the behavior of a subparsers argument. Doing your own tests after parse_args
is often simpler than making argparse
do something special.
Solution 2
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-a','--all', action='store_true', \
help = "Stops all processes")
group.add_argument('-n','--name', \
help = "Stops the named process")
print parser.parse_args()
./tester.py -h
usage: zx.py [-h] (-a | -n NAME)
optional arguments:
-h, --help show this help message and exit
-a, --all Stops all processes
-n NAME, --name NAME Stops the named process
Alex
Updated on June 28, 2022Comments
-
Alex almost 2 years
I would like to use
argparse
to make some code to be used in the following two ways:./tester.py all ./tester.py name someprocess
i.e. either
all
is specified ORname
with some additional string.I have tried to implement as follows:
import argparse parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() group.add_argument('all', action='store_true', \ help = "Stops all processes") group.add_argument('name', \ help = "Stops the named process") print parser.parse_args()
which gives me an error
ValueError: mutually exclusive arguments must be optional
Any idea how to do it right? I also would like to avoid sub parsers in this case.
-
justhalf about 10 years+1 simplest indeed, I forgot about
choices
, and I didn't read carefully that OP only needs one process name aftername
argument. -
Борат Сагдиев over 2 yearsThis is not good enough. The default help gives no indication that
process
is required withname
and ignored / disallowed withall
. Eight years later nobody bothered to implement such functionality in argparse. I don't see why mutually exclusive groups couldn't contain positional arguments, as long as there is only a single positional argument in the entire argument parser (as in OP's case and mine). -
hpaulj over 2 years@БоратСагдиев, I pointed out that one positional with the right
nargs
(and a default) can be used in such a group. The OP had the wrong kind of positional. Formatting the usage line for a fancy set of conditions is not a trivial task. Write your own, or use thedescription/epilog
to explain what you require. -
Борат Сагдиев over 2 yearsIndeed, I tried it with an option and a positional argument with
nargs='?'
and it seems to work, for the most part. When I first add the option to the group, and then the positional argument, the help correctly indicates that the two arguments are mutually exclusive. However, when the positional is added first and then the option, it just shows[-o] [positional]
as if they could be used in conjunction, although actually using them in conjunction causes an error, as expected. -
hpaulj over 2 years@БоратСагдиев, I don't remember the details why, but yes, tor correct usage display, the positional should be last of the group. The usage formatter is not very sophisticated and easily broken. But during parsing the groups testing is performed by unrelated code. When I explored implementing a more general nested groups mechanism some years ago, the usage formatting was a much more complicated task than either the input (defining groups) or testing.