Reputation: 1712
I want the user to be able to pass a mandatory argument to 'argparse', with either positional, or optional argument.
I.e., both following forms are valid:
my_prog arg
my_prog -m arg
I've seen Argparse optional positional arguments?
But the suggestions there make both form optional. I want that one of them will be mandatory.
Of course, I can add a manual checking after parsing that at least one of them has been set. But I got a hunch that there must be a better solution.
(And even with my manual approach, the 'help' section shows both of them as optional)
Upvotes: 5
Views: 6177
Reputation: 231738
The mutually exclusive group
mechanism can take a required
parameter. It also works with one ?
positional along with the optionals (flagged arguments). (more than one '?' positional doesn't make sense).
As for the help
display there are 2 default groups, positonal
and optional
. So even if an optional
(flagged) is set to required
it is, by default, displayed in the optional
group. The usage
line is a better guide as to whether an argument is required or not. If you don't like the group labels in the help section, define your own argument groups.
In [146]: import argparse
In [147]: parser = argparse.ArgumentParser()
In [148]: gp = parser.add_mutually_exclusive_group(required=True)
In [149]: gp.add_argument('pos', nargs='?', default='foo');
In [150]: gp.add_argument('-f','--foo', default='bar');
In [151]: parser.parse_args('arg'.split())
Out[151]: Namespace(foo='bar', pos='arg')
In [152]: parser.parse_args('-f arg'.split())
Out[152]: Namespace(foo='arg', pos='foo')
In [153]: parser.parse_args('arg -f arg'.split())
usage: ipython3 [-h] [-f FOO] [pos]
ipython3: error: argument -f/--foo: not allowed with argument pos
In [154]: parser.parse_args(''.split())
usage: ipython3 [-h] [-f FOO] [pos]
ipython3: error: one of the arguments pos -f/--foo is required
In [155]: parser.parse_args('-h'.split())
usage: ipython3 [-h] [-f FOO] [pos]
positional arguments:
pos
optional arguments:
-h, --help show this help message and exit
-f FOO, --foo FOO
Oops, usage isn't showing the -f
and pos
in a mutually exlusive group. Sometimes that usage
formatting is brittle.
Switching the order in which the arguments are defined gives a better usage
In [156]: parser = argparse.ArgumentParser()
In [157]: gp = parser.add_mutually_exclusive_group(required=True)
In [158]: gp.add_argument('-f','--foo', default='bar');
In [159]: gp.add_argument('pos', nargs='?', default='foo');
In [160]:
In [160]: parser.parse_args('-h'.split())
usage: ipython3 [-h] (-f FOO | pos)
positional arguments:
pos
optional arguments:
-h, --help show this help message and exit
-f FOO, --foo FOO
With a user defined argument group:
In [165]: parser = argparse.ArgumentParser()
In [166]: gp = parser.add_argument_group('Mutually exclusive')
In [167]: gpm = gp.add_mutually_exclusive_group(required=True)
In [168]: gpm.add_argument('-f','--foo', default='bar');
In [169]: gpm.add_argument('pos', nargs='?', default='foo');
In [170]:
In [170]: parser.parse_args('-h'.split())
usage: ipython3 [-h] (-f FOO | pos)
optional arguments:
-h, --help show this help message and exit
Mutually exclusive:
-f FOO, --foo FOO
pos
This is the one exception to the general rule argument_groups and mutually_exclusive_groups aren't designed for nesting.
The m-x-group was not required, usage would use []
usage: ipython3 [-h] [-f FOO | pos]
Upvotes: 8