I’m writing a program in which I would like to have arguments like this:
--[no-]foo Do (or do not) foo. Default is do.
Is there a way to get argparse to do this for me?
Advertisement
Answer
v3.9 has added an action
class that does this. From the docs (near the end of the action
section)
The
BooleanOptionalAction
is available inargparse
and adds support for boolean actions such as--foo
and--no-foo
:>>> import argparse >>> parser = argparse.ArgumentParser() >>> parser.add_argument('--foo', action=argparse.BooleanOptionalAction) >>> parser.parse_args(['--no-foo']) Namespace(foo=False)
To explore @wim’s comment about not being mutually_exclusive.
In [37]: >>> parser = argparse.ArgumentParser() ...: >>> parser.add_argument('--foo', action=argparse.BooleanOptionalAction) Out[37]: BooleanOptionalAction(option_strings=['--foo', '--no-foo'], dest='foo', nargs=0, const=None, default=None, type=None, choices=None, help=None, metavar=None)
The last line shows that the add_argument
created a BooleanOptionalAction
Action class.
With various inputs:
In [38]: parser.parse_args('--foo'.split()) Out[38]: Namespace(foo=True) In [39]: parser.parse_args('--no-foo'.split()) Out[39]: Namespace(foo=False) In [40]: parser.parse_args([]) Out[40]: Namespace(foo=None) In [41]: parser.parse_args('--no-foo --foo'.split()) Out[41]: Namespace(foo=True)
So you can supply both flags, with the last taking effect, over writing anything produced by the previous. It’s as though we defined two Actions
, with the same dest
, but different True/False
const.
The key is that it defined two flag strings:
option_strings=['--foo', '--no-foo']
Part of the code for this new class:
class BooleanOptionalAction(Action): def __init__(self, option_strings, dest, ...): _option_strings = [] for option_string in option_strings: _option_strings.append(option_string) if option_string.startswith('--'): option_string = '--no-' + option_string[2:] _option_strings.append(option_string) ... def __call__(self, parser, namespace, values, option_string=None): if option_string in self.option_strings: setattr(namespace, self.dest, not option_string.startswith('--no-'))
So the action __init__
defines the two flags, and the __call__
checks for the no
part.