Reputation: 5300
usage: [-h] [--foo FOO] bar
abc
if I execute my script like below./myscript.py --foo bar
--> bar
is positional argument here. but args.foo
is considering bar
as argument of '--foo'
. I want args.foo
to be abc
and bar
to be the positional argument
Upvotes: 4
Views: 8153
Reputation: 231738
Have you tried using --
to separate the --foo
argument(s) from the bar
?
With this basic setup:
p=argparse.ArgumentParser()
p.add_argument('--foo',nargs='?',const='abc', default='other')
p.add_argument('bar')
In [633]: p.parse_args(['bar'])
Out[633]: Namespace(bar='bar', foo='other')
In [634]: p.parse_args(['bar','--foo'])
Out[634]: Namespace(bar='bar', foo='abc')
In [635]: p.parse_args(['bar','--foo','test'])
Out[635]: Namespace(bar='bar', foo='test')
In [636]: p.parse_args(['--foo','bar'])
usage: ipython3 [-h] [--foo [FOO]] bar
'error message'
The plain --foo
can occur AFTER bar
. 'other' ('default) is the value it gets if absent, 'abc' (the
const`) the value it gets if present but 'empty'.
But as you found out, when --foo
is first it consumes the following string, leaving nothing for the positional
argument. In other words, when handling --foo
, it does not take into account the future needs of bar
.
If I add another argument
p.add_argument('--baz',action='store_true')
p.parse_args(['--foo','--baz','bar'])
# Namespace(bar='bar', baz=True, foo='abc')
This works because --baz
marks the end of --foo
arguments.
You can also use --
to mark the end of optionals
and the start of postionals
:
p.parse_args(['--foo','--','bar'])
There is a bug issue
that tries to rectify this by reserving strings for trailing positionals
http://bugs.python.org/issue9338 argparse optionals with nargs='?', '*' or '+' can't be followed by positionals
But the patch is not trivial. --
is your best tool at this time.
I don't follow your comment to Martijn about mutually exclusive group(s).
Upvotes: 1
Reputation: 1125318
You can't, not without reworking your arguments.
You should use two switches here; one to switch on the behaviour, and one to override the default value picked. You can make the second switch imply the first:
usage: [-h] [--foo] [--foo-setting FOO] bar
where --foo
is a boolean switch that toggles the behaviour, and --foo-setting
lets you set the configuration for the switch:
parser.add_argument('--foo', action='store_true')
parser.add_argument('--foo-setting', help='Set the value for foo, implies --foo is given')
# ...
args = parser.parse_args()
if args.foo_setting is not None:
# default was overridden, explicitly enable the foo feature
args.foo = True
if args.foo:
foo_feature_setting = args.foo_setting or 'default value'
Upvotes: 4