I would like to use argparse to parse boolean command-line arguments written as "--foo True" or "--foo False". For example:
my_program --my_boolean_flag False
However, the following test code does not do what I would like:
import argparse
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument("--my_bool", type=bool)
cmd_line = ["--my_bool", "False"]
parsed_args = parser.parse(cmd_line)
Sadly, parsed_args.my_bool
evaluates to True
. This is the case even when I change cmd_line
to be ["--my_bool", ""]
, which is surprising, since bool("")
evalutates to False
.
How can I get argparse to parse "False"
, "F"
, and their lower-case variants to be False
?
parser.add_argument('--feature', dest='feature', default=False, action='store_true')
. This solution will gurantee you always get a bool
type with value True
or False
. (This solution has a constraint: your option must have a default value.)
parser.add_argument('--feature', dest='feature', type=lambda x:bool(distutils.util.strtobool(x)))
. When the option is used, this solution will ensure a bool
type with value of True
or False
. When the option is not used you will get None
. (distutils.util.strtobool(x)
is from another stackoverflow question)
parser.add_argument('--my_bool', action='store_true', default=False)
import distutils.util
instead of import disutils
. See this answer
I think a more canonical way to do this is via:
command --feature
and
command --no-feature
argparse
supports this version nicely:
parser.add_argument('--feature', action=argparse.BooleanOptionalAction)
Python < 3.9:
parser.add_argument('--feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
Of course, if you really want the --arg <True|False>
version, you could pass ast.literal_eval
as the "type", or a user defined function ...
def t_or_f(arg):
ua = str(arg).upper()
if 'TRUE'.startswith(ua):
return True
elif 'FALSE'.startswith(ua):
return False
else:
pass #error condition maybe?
Yet another solution using the previous suggestions, but with the "correct" parse error from argparse
:
def str2bool(v):
if isinstance(v, bool):
return v
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
This is very useful to make switches with default values; for instance
parser.add_argument("--nice", type=str2bool, nargs='?',
const=True, default=False,
help="Activate nice mode.")
allows me to use:
script --nice
script --nice <bool>
and still use a default value (specific to the user settings). One (indirectly related) downside with that approach is that the 'nargs' might catch a positional argument -- see this related question and this argparse bug report.
str2bool(v)
could be replaced with bool(distutils.util.strtobool(v))
. Source: stackoverflow.com/a/18472142/2436175
if args.nice:
beacuse if the argument is set to False, it will never pass the condition. If this is right then maybe it is better to return list from str2bool
function and set list as const
parameter, like this [True]
, [False]
. Correct me if I am wrong
If you want to allow --feature and --no-feature at the same time (last one wins)
This allows users to make a shell alias with --feature
, and overriding it with --no-feature
.
Python 3.9 and above
parser.add_argument('--feature', default=True, action=argparse.BooleanOptionalAction)
Python 3.8 and below
I recommend mgilson's answer:
parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
If you DON'T want to allow --feature and --no-feature at the same time
You can use a mutually exclusive group:
feature_parser = parser.add_mutually_exclusive_group(required=False)
feature_parser.add_argument('--feature', dest='feature', action='store_true')
feature_parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
You can use this helper if you are going to set many of them:
def add_bool_arg(parser, name, default=False):
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('--' + name, dest=name, action='store_true')
group.add_argument('--no-' + name, dest=name, action='store_false')
parser.set_defaults(**{name:default})
add_bool_arg(parser, 'useful-feature')
add_bool_arg(parser, 'even-more-useful-feature')
add_argument
is called with dest='feature'
. set_defaults
is called with feature=True
. Understand?
--flag False
, part of SO answers should be about WHAT they're trying to solve, not just about HOW. There should be absolutely no reason to do --flag False
or --other-flag True
and then use some custom parser to convert the string to a boolean.. action='store_true'
and action='store_false'
are the best ways to use boolean flags
... can be “don’t do that”, but it should also include “try this instead”
which (at least to me) implies answers should go deeper when appropriate. There are definitely times when some of us posting questions can benefit from guidance on better/best practices, etc.. Answering "as stated" often doesn't do that. That being said, your frustration with answers often assuming too much (or incorrectly) is completely valid.
parser.set_defaults(feature=None)
help=
entry for this argument, where should it go? In the add_mutually_exclusive_group()
call? In one or both of the add_argument()
calls? Somewhere else?
Here is another variation without extra row/s to set default values. The boolean value is always assigned, so that it can be used in logical statements without checking beforehand:
import argparse
parser = argparse.ArgumentParser(description="Parse bool")
parser.add_argument("--do-something", default=False, action="store_true",
help="Flag to do something")
args = parser.parse_args()
if args.do_something:
print("Do something")
else:
print("Don't do something")
print(f"Check that args.do_something={args.do_something} is always a bool.")
required=True
or else you'll always get a True arg.
python3 test.py --do-something False
fails with error: unrecognized arguments: False
, so it does not really answer the question.
oneliner:
parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
type=lambda x: (str(x).lower() in ['true','1', 'yes'])
distutils.utils.strtobool
, eg type=lambda x: bool(strtobool(str(x)))
. True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0.
There seems to be some confusion as to what type=bool
and type='bool'
might mean. Should one (or both) mean 'run the function bool()
, or 'return a boolean'? As it stands type='bool'
means nothing. add_argument
gives a 'bool' is not callable
error, same as if you used type='foobar'
, or type='int'
.
But argparse
does have registry that lets you define keywords like this. It is mostly used for action
, e.g. `action='store_true'. You can see the registered keywords with:
parser._registries
which displays a dictionary
{'action': {None: argparse._StoreAction,
'append': argparse._AppendAction,
'append_const': argparse._AppendConstAction,
...
'type': {None: <function argparse.identity>}}
There are lots of actions defined, but only one type, the default one, argparse.identity
.
This code defines a 'bool' keyword:
def str2bool(v):
#susendberg's function
return v.lower() in ("yes", "true", "t", "1")
p = argparse.ArgumentParser()
p.register('type','bool',str2bool) # add type keyword to registries
p.add_argument('-b',type='bool') # do not use 'type=bool'
# p.add_argument('-b',type=str2bool) # works just as well
p.parse_args('-b false'.split())
Namespace(b=False)
parser.register()
is not documented, but also not hidden. For the most part the programmer does not need to know about it because type
and action
take function and class values. There are lots of stackoverflow examples of defining custom values for both.
In case it isn't obvious from the previous discussion, bool()
does not mean 'parse a string'. From the Python documentation:
bool(x): Convert a value to a Boolean, using the standard truth testing procedure.
Contrast this with
int(x): Convert a number or string x to an integer.
A quite similar way is to use:
feature.add_argument('--feature',action='store_true')
and if you set the argument --feature in your command
command --feature
the argument will be True, if you do not set type --feature the arguments default is always False!
--feature False
Simplest & most correct way is:
from distutils.util import strtobool
parser.add_argument('--feature', dest='feature',
type=lambda x: bool(strtobool(x)))
Do note that True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0. Raises ValueError if val is anything else.
I was looking for the same issue, and imho the pretty solution is :
def str2bool(v):
return v.lower() in ("yes", "true", "t", "1")
and using that to parse the string to boolean as suggested above.
distutils.util.strtobool(v)
.
distutils.util.strtobool
returns 1 or 0, not an actual boolean.
This works for everything I expect it to:
add_boolean_argument(parser, 'foo', default=True)
parser.parse_args([]) # Whatever the default was
parser.parse_args(['--foo']) # True
parser.parse_args(['--nofoo']) # False
parser.parse_args(['--foo=true']) # True
parser.parse_args(['--foo=false']) # False
parser.parse_args(['--foo', '--nofoo']) # Error
The code:
def _str_to_bool(s):
"""Convert string to bool (in argparse context)."""
if s.lower() not in ['true', 'false']:
raise ValueError('Need bool; got %r' % s)
return {'true': True, 'false': False}[s.lower()]
def add_boolean_argument(parser, name, default=False):
"""Add a boolean argument to an ArgumentParser instance."""
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--' + name, nargs='?', default=default, const=True, type=_str_to_bool)
group.add_argument('--no' + name, dest=name, action='store_false')
_str_to_bool(s)
to convert s = s.lower()
once, then test if s not in {'true', 'false', '1', '0'}
, and finally return s in {'true', '1'}
.
In addition to what @mgilson said, it should be noted that there's also a ArgumentParser.add_mutually_exclusive_group(required=False)
method that would make it trivial to enforce that --flag
and --no-flag
aren't used at the same time.
Simplest. It's not flexible, but I prefer simplicity.
parser.add_argument('--boolean_flag',
help='This is a boolean flag.',
type=eval,
choices=[True, False],
default='True')
EDIT: If you don't trust the input, don't use eval
.
eval
is a built-in function. docs.python.org/3/library/functions.html#eval This can be any unary function which other, more flexible approaches take advantage.
ast.literal_eval
which is safer.
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.
New in version 3.9
and I cannot import BooleanOptionalAction
from argparse
in 3.7...
action=argparse.BooleanOptionalAction
works. Earlier under type
, it warns against using the type=bool
, "The bool() function is not recommended as a type converter. All it does is convert empty strings to False and non-empty strings to True. This is usually not what is desired."
.
args = ap.parse_args(['--foo', 'False'])
returns True (and in my opinion it shouldn't
A simpler way would be to use as below.
parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
After previously following @akash-desarda 's excellence answer https://stackoverflow.com/a/59579733/315112 , to use strtobool
via lambda
, later, I decide to use strtobool
directly instead.
import argparse
from distutils import util
parser.add_argument('--feature', type=util.strtobool)
Yes you're right, strtobool
is returning an int
, not a bool
. But strtobool
will not returning any other value except 0
and 1
, and python will get them converted to a bool
value seamlessy and consistently.
>>> 0 == False
True
>>> 0 == True
False
>>> 1 == False
False
>>> 1 == True
True
While on receiving a wrong input value like
python yours.py --feature wrong_value
An argparse.Action with strtobool
compared to lambda
will produce a slightly clearer/comprehensible error message:
yours.py: error: argument --feature: invalid strtobool value: 'wrong_value'
Compared to this code,
parser.add_argument('--feature', type=lambda x: bool(util.strtobool(x))
Which will produce a less clear error message:
yours.py: error: argument --feature: invalid <lambda> value: 'wrong_value'
Simplest way would be to use choices:
parser = argparse.ArgumentParser()
parser.add_argument('--my-flag',choices=('True','False'))
args = parser.parse_args()
flag = args.my_flag == 'True'
print(flag)
Not passing --my-flag evaluates to False. The required=True option could be added if you always want the user to explicitly specify a choice.
As an improvement to @Akash Desarda 's answer, you could do
import argparse
from distutils.util import strtobool
parser = argparse.ArgumentParser()
parser.add_argument("--foo",
type=lambda x:bool(strtobool(x)),
nargs='?', const=True, default=False)
args = parser.parse_args()
print(args.foo)
And it supports python test.py --foo
(base) [costa@costa-pc code]$ python test.py
False
(base) [costa@costa-pc code]$ python test.py --foo
True
(base) [costa@costa-pc code]$ python test.py --foo True
True
(base) [costa@costa-pc code]$ python test.py --foo False
False
I think the most canonical way will be:
parser.add_argument('--ensure', nargs='*', default=None)
ENSURE = config.ensure is None
Quick and easy, but only for arguments 0 or 1:
parser.add_argument("mybool", default=True,type=lambda x: bool(int(x)))
myargs=parser.parse_args()
print(myargs.mybool)
The output will be "False" after calling from terminal:
python myscript.py 0
parser.add_argument("mybool", default=True,type=lambda x: bool(int(x)), choices=['0','1'])
class FlagAction(argparse.Action):
# From http://bugs.python.org/issue8538
def __init__(self, option_strings, dest, default=None,
required=False, help=None, metavar=None,
positive_prefixes=['--'], negative_prefixes=['--no-']):
self.positive_strings = set()
self.negative_strings = set()
for string in option_strings:
assert re.match(r'--[A-z]+', string)
suffix = string[2:]
for positive_prefix in positive_prefixes:
self.positive_strings.add(positive_prefix + suffix)
for negative_prefix in negative_prefixes:
self.negative_strings.add(negative_prefix + suffix)
strings = list(self.positive_strings | self.negative_strings)
super(FlagAction, self).__init__(option_strings=strings, dest=dest,
nargs=0, const=None, default=default, type=bool, choices=None,
required=required, help=help, metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None):
if option_string in self.positive_strings:
setattr(namespace, self.dest, True)
else:
setattr(namespace, self.dest, False)
Similar to @Akash but here is another approach that I've used. It uses str
than lambda
because python lambda
always gives me an alien-feelings.
import argparse
from distutils.util import strtobool
parser = argparse.ArgumentParser()
parser.add_argument("--my_bool", type=str, default="False")
args = parser.parse_args()
if bool(strtobool(args.my_bool)) is True:
print("OK")
just do the following , you can make --test = True
by using
python filename --test
parser.add_argument("--test" , default=False ,help="test ?", dest='test', action='store_true')
Convert the value:
def __arg_to_bool__(arg):
"""__arg_to_bool__
Convert string / int arg to bool
:param arg: argument to be converted
:type arg: str or int
:return: converted arg
:rtype: bool
"""
str_true_values = (
'1',
'ENABLED',
'ON',
'TRUE',
'YES',
)
str_false_values = (
'0',
'DISABLED',
'OFF',
'FALSE',
'NO',
)
if isinstance(arg, str):
arg = arg.upper()
if arg in str_true_values:
return True
elif arg in str_false_values:
return False
if isinstance(arg, int):
if arg == 1:
return True
elif arg == 0:
return False
if isinstance(arg, bool):
return arg
# if any other value not covered above, consider argument as False
# or you could just raise and error
return False
[...]
args = ap.parse_args()
my_arg = options.my_arg
my_arg = __arg_to_bool__(my_arg)
Expanding on gerardw's answer
The reason parser.add_argument("--my_bool", type=bool)
doesn't work is that bool("mystring")
is True
for any non-empty string so bool("False")
is actually True
.
What you want is
my_program.py
import argparse
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument(
"--my_bool",
choices=["False", "True"],
)
parsed_args = parser.parse_args()
my_bool = parsed_args.my_bool == "True"
print(my_bool)
$ python my_program.py --my_bool False
False
$ python my_program.py --my_bool True
True
$ python my_program.py --my_bool true
usage: my_program.py [-h] [--my_bool {False,True}]
my_program.py: error: argument --my_bool: invalid choice: 'true' (choose from 'False', 'True')
I found good way to store default value of parameter as False and when it is present in commandline argument then its value should be true.
cmd command when you want argument to be true: python main.py --csv
when you want your argument should be false: python main.py
import argparse
from ast import parse
import sys
parser = argparse.ArgumentParser(description='')
parser.add_argument('--csv', action='store_true', default = False
,help='read from csv')
args = parser.parse_args()
if args.csv:
print('reading from csv')
False
.
Success story sharing
type=bool
should work out of the box (consider positional arguments!). Even when you additionally specifychoices=[False,True]
, you end up with both "False" and "True" considered True (due to a cast from string to bool?). Maybe related issuebool()
function should do, or what argparse should accept intype=fn
? Allargparse
checks is thatfn
is callable. It expectsfn
to take one string argument, and return a value. The behavior offn
is the programer's responsibility, notargparse's
.