Argparse optional boolean [duplicate]
Are you sure you need that pattern? --foo
and --foo <value>
, together, for a boolean switch, is not a common pattern to use.
As for your issue, remember that the command line value is a string and, type=bool
means that you want bool(entered-string-value)
to be applied. For --foo False
that means bool("False")
, producing True
; all non-empty strings are true! See Why is argparse not parsing my boolean flag correctly? as well.
Instead of supporting --foo
/ --foo <string value>
, I would strongly recommend you use --foo
to mean True
, drop the argument value, and instead add a --no-foo
option to explicitly set False
:
parser.add_argument('--foo', default=False, action='store_true')parser.add_argument('--no-foo', dest='foo', action='store_false')
The dest='foo'
addition on the --no-foo
switch ensures that the False
value it stores (via store_false
) ends up on the same args.foo
attribute.
As of Python 3.9, you can also use the argparse.BooleanOptionalAction
action class:
parser.add_argument("--foo", action=argparse.BooleanOptionalAction)
and it'll have the same effect, handling --foo
and --no-foo
to set and clear the flag.
You'd only need a --foo / --no-foo
combination if you have some other configuration mechanism that would set foo
to True
and you needed to override this again with a command-line switch. --no-<option>
is a widely adopted standard to invert a boolean command-line switch.
If you don't have a specific need for a --no-foo
inverted switch (since just omitting --foo
would already mean 'false'), then just stick with the action='store_true'
option. This keeps your command line simple and clear!
However, if your use case or other constraints specifically require that your command line must have some king of --foo (true|false|0|1)
support, then add your own converter:
def str_to_bool(value): if isinstance(value, bool): return value if value.lower() in {'false', 'f', '0', 'no', 'n'}: return False elif value.lower() in {'true', 't', '1', 'yes', 'y'}: return True raise ValueError(f'{value} is not a valid boolean value')parser.add_argument('--foo', type=str_to_bool, nargs='?', const=True, default=False)
- the
const
value is used fornargs='?'
arguments where the argument value is omitted. Here that setsfoo=True
when--foo
is used. default=False
is used when the switch is not used at all.type=str_to_bool
is used to handle the--foo <value>
case.
Demo:
$ cat so52403065.pyfrom argparse import ArgumentParserparser = ArgumentParser()def str_to_bool(value): if value.lower() in {'false', 'f', '0', 'no', 'n'}: return False elif value.lower() in {'true', 't', '1', 'yes', 'y'}: return True raise ValueError(f'{value} is not a valid boolean value')parser.add_argument('--foo', type=str_to_bool, nargs='?', const=True, default=False)print(parser.parse_args())$ python so52403065.pyNamespace(foo=False)$ python so52403065.py --fooNamespace(foo=True)$ python so52403065.py --foo TrueNamespace(foo=True)$ python so52403065.py --foo noNamespace(foo=False)$ python so52403065.py --foo arrbuggrhellnousage: so52403065.py [-h] [--foo [FOO]]so52403065.py: error: argument --foo: invalid str_to_bool value: 'arrbuggrhellno'
You should use the action='store_true'
parameter instead for Boolean arguments:
parser.add_argument('--foo', action='store_true')
So that the absence of the --foo
option:
python test.py
would result in a False
value for the foo
argument, and the presence of the --foo
option:
python test.py --foo
would result in a True
value for the foo
argument.