Parsing boolean values with argparse
This is actually outdated. For Python 3.7+, Argparse now supports boolean args (search BooleanOptionalAction).
The implementation looks like this:
import argparse
ap = argparse.ArgumentParser()
# List of args
ap.add_argument('--foo', default=True, type=bool, help='Some helpful text that is not bar. Default = True')
# Importable object
args = ap.parse_args()
One other thing to mention: this will block all entries other than True and False for the argument via argparse.ArgumentTypeError. You can create a custom error class for this if you want to try to change this for any reason.
Cannot parse boolean values with argparse
The way 'store_true' works is that if you give --download
as an argument, then the value is true; and if you omit it, it is false.
The reason type=bool
doesn't work as you want is that any nonempty string passed to the bool
function will result in True. (You could, if you wanted, write a function that returns True if the string is "True" and False if it is "False", and use that for type
, but that is not the typical use-case.)
Argparse not parsing boolean arguments?
You are misunderstanding how the argparse
understands the boolean arguments.
Basically you should use action='store_true'
or action='store_false'
instead of the default value, with the understanding that not specifying the argument will give you the opposite of the action, e.g.
parser.add_argument('-x', type=bool, action='store_true')
will cause:
python3 command -x
to have x
set to True
and
python3 command
to have x
set to False
.
While action=store_false
will do the opposite.
Setting bool
as type does not behave as you expect and this is a known issue.
The reason for the current behavior is that type
is expected to be a callable which is used as argument = type(argument)
. bool('False')
evaluates to True
, so you need to set a different type
for the behavior you expect to happen.
Argparse optional boolean
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.py
from argparse import ArgumentParser
parser = 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.py
Namespace(foo=False)
$ python so52403065.py --foo
Namespace(foo=True)
$ python so52403065.py --foo True
Namespace(foo=True)
$ python so52403065.py --foo no
Namespace(foo=False)
$ python so52403065.py --foo arrbuggrhellno
usage: so52403065.py [-h] [--foo [FOO]]
so52403065.py: error: argument --foo: invalid str_to_bool value: 'arrbuggrhellno'
Why is argparse not parsing my boolean flag correctly?
You are trying to turn the string "False"
into a boolean:
>>> bool("False")
True
That won't work because the string "False"
is a non-empty value. All non-empty strings have a True
boolean value.
Use a store_false
action instead:
parser.add_argument('--disable-feature', dest='feature',
action='store_false')
Now when you use that switch, False
is stored, otherwise the default is True
(set by action='store_false'
).
R: Parsing boolean command line argument in using argparse
I don't know R
, but the description of this package says it's a wrapper for the Python argparse
.
I would recommend changing these:
parser$add_argument("is_local", nargs='?', type="logical",
help="whether to use local or server path", default=FALSE)
parser$add_argument("alert", nargs='?', type="double",
help="alert threshold", default=0.99)
to
parser$add_argument("--local", action='store_true'),
help="whether to use local or server path")
parser$add_argument("--alert", type="double",
help="alert threshold", default=0.99)
which would be called with
Rscript my_func.R --local --alert 0.99
store_true
is illustrated on the basic docs page, https://github.com/trevorld/r-argparse
If I read the R
correctly, your is_local
should be giving you a warning
"You almost certainly want to use action='store_true' or action='store_false' instead"
A store_true
argument sets the attribute to TRUE
if present, and the default FALSE
if absent. It should be an optional
(--) and not set the nargs
.
(It is possible to have an argument that takes strings 'true' and 'false' (or any other pair in your native language) and converts them to logical values, but requires more coding.)
I made --alert
a flagged argument as well, without the nargs. Its value will be the default if absent, and the convert the string to a double
if provided. It could be a '?' positional, but while learning I think it's best to stick with optionals
unless you want the argument to be required.
The R-argparse
docs aren't very complete. You may need to refer to the Python docs, and experiment to get the translation right.
https://docs.python.org/3/library/argparse.html
Why in argparse, a 'True' is always 'True'?
You are not passing in the False
object. You are passing in the 'False'
string, and that's a string of non-zero length.
Only a string of length 0 tests as false:
>>> bool('')
False
>>> bool('Any other string is True')
True
>>> bool('False') # this includes the string 'False'
True
Use a store_true
or store_false
action instead. For default=True
, use store_false
:
parser.add_argument('--bool', default=True, action='store_false', help='Bool type')
Now omitting the switch sets args.bool
to True
, using --bool
(with no further argument) sets args.bool
to False
:
python test.py
True
python test.py --bool
False
If you must parse a string with True
or False
in it, you'll have to do so explicitly:
def boolean_string(s):
if s not in {'False', 'True'}:
raise ValueError('Not a valid boolean string')
return s == 'True'
and use that as the conversion argument:
parser.add_argument('--bool', default=True, type=boolean_string, help='Bool type')
at which point --bool False
will work as you expect it to.
Related Topics
How to Execute a File Within the Python Interpreter
Count Unique Values Per Groups with Pandas
How to Install Python MySQLdb Module Using Pip
Iterate Over Model Instance Field Names and Values in Template
Logging Uncaught Exceptions in Python
How to Fake Terminal Input with Termios.Tiocsti
Apt-Get Install for Different Python Versions
Running a Linux Command from Python
Python Ta-Lib Install Error, How Solve It
Postgresql: How to Install Plpythonu Extension
Why Is a List Comprehension So Much Faster Than Appending to a List
How Is the 'Is' Keyword Implemented in Python
Best Way to Join/Merge by Range in Pandas
Show Default Value for Editing on Python Input Possible
Can Pandas Automatically Read Dates from a CSV File