Argparse optional boolean [duplicate] Argparse optional boolean [duplicate] python-3.x python-3.x

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 for nargs='?' arguments where the argument value is omitted. Here that sets foo=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.