3
usage:  [-h] [--foo FOO] bar
  1. How do I make sure that default value of FOO is 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

tez
  • 4,990
  • 12
  • 47
  • 67

2 Answers2

3

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'
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • The problem is, I want to add to mutual exclusive group. Is it possible to have multiple option in one set of the group? – tez Apr 24 '15 at 13:49
  • @tez: no; but you are free to add additional tests after parsing. If `foo_setting` was set but another mutually exclusive group option was picked, then that's an error you need to manually detect. – Martijn Pieters Apr 24 '15 at 13:56
1

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' (theconst`) 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).

hpaulj
  • 221,503
  • 14
  • 230
  • 353