Be careful when dealing with Boolean values in Python’s argparse

Money and Business

To handle command line arguments in Python, use the argv or argparse modules of the sys module.

The argparse module allows for flexible handling of command line arguments, but care must be taken when dealing with Boolean values (true, false).

The following information is provided here.

  • argparse for easy definition of arguments
  • Specify the type of the argument (type) with argparse
  • Do not specify “bool” as argument type of add_argument()
  • Judgment by bool()
  • Use the argument action instead of the argument type.
  • Using the strtobool() function

argparse for easy definition of arguments

The argparse module makes it easy to define command line arguments.

The argparse module makes it easy to create user-friendly command line interfaces. You define what arguments your program needs, and argparse will figure out how to parse those options from sys.argv. argparse module automatically generates help and usage messages, and raises an error if the user specifies invalid arguments to the program. error when the user specifies invalid arguments to the program.
argparse — Parser for command-line options, arguments and sub-commands — Python 3.10.0 Documentation

Specify the type of the argument (type) with argparse

A useful feature of argparse is to specify the type (type).

For example, if you specify an integer (int) type, it will automatically convert the argument to int and also raise an error for arguments that are not int.

The type is specified by the argument type of add_argument().

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('arg_int', type=int)

args = parser.parse_args()
print(args.arg_int)
print(type(args.arg_int))

Run this file from the command line.

$ python argparse_type_int.py 100
100
<type 'int'>

Argument 100 is read as int.

If a non-int value is used as an argument, an error will occur.

$ python argparse_type_int.py foo
usage: argparse_type_int.py [-h] arg_int
argparse_type_int.py: error: argument arg_int: invalid int value: 'foo'

$ python argparse_type_int.py 1.23
usage: argparse_type_int.py [-h] arg_int
argparse_type_int.py: error: argument arg_int: invalid int value: '1.23'

Very useful for playing unexpected arguments.

Do not specify “bool” as argument type of add_argument()

It is important to note that bool, like int and float, will not work as expected if you specify bool as the argument type of add_argument().

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('arg_bool', type=bool)

args = parser.parse_args()
print(args.arg_bool)
print(type(args.arg_bool))

Run this file from the command line.

$ python argparse_type_bool.py True
True
<type 'bool'>

If true is used as an argument, it will be read as a bool type true. This is the expected behavior, but the problem is the following case.

$ python argparse_type_bool.py False
True
<type 'bool'>

$ python argparse_type_bool.py bar
True
<type 'bool'>

If you use false or any other string as an argument, it will be read as true.

The reason why this happens is that when type=xxx is specified in add_argument(), the argument is passed to xxx().

For example, if type=int, the argument will be passed to int(); if type=float, then float().

The same is true for type=bool, which means the argument will be passed to bool().

Judgment by bool()

This bool() is a tricky one.

The following values are considered false:

  • None
  • false
  • Zero in numeric types. For example, the following values
    • 0
    • 0.0
    • 0j
  • An empty sequence. For example
    • ''
    • ()
    • []
  • Empty mapping. For example
    • {}

All other values are assumed to be true – thus objects of many types are always true. Operations and built-in functions that return Boolean results always return 0 or False as the false value and 1 or True as the true value, unless otherwise noted.

Therefore, all non-empty strings passed to bool(), whether 'true' or 'false', will return true. Only empty strings will be false.

print(bool('True'))
print(bool('False'))
print(bool('abc'))
# True
# True
# True

print(bool(''))
# False

When type=bool is set in add_argument(), the argument is passed to bool(). Therefore, as shown in the example above, if false is used as the argument, it will be converted by bool() as the string 'False' and read as true.

Use the argument action instead of the argument type.

If you want to use Boolean values in argparse, specify 'store_true' or 'store_false' for the argument action.

  • 'store_true'
  • 'store_false'

These will be special versions of 'store_const' that will store True and False respectively. In addition, they will set the default values to False and True respectively, in that order.
argparse — Parser for command-line options, arguments and sub-commands — Python 3.10.0 Documentation

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--en', action='store_true')

args = parser.parse_args()
print(args.en)
print(type(args.en))

In this example, the following options are given.
--enTherefore, if en is not set as true, it will be loaded as false, which is the default value of en.

$ python argparse_option_bool.py --en
True
<type 'bool'>

$ python argparse_option_bool.py
False
<type 'bool'>

If you want to set the default to true, and false when the option is added, just do the following.
action='store_false'

Using the strtobool() function

If you want to use positional arguments instead of options, you can also use the function strtobool().

strtobool() is a function that converts a string to true (1) or false (0).

Converts a boolean string to true (1) or false (0).
The true values are as follows

  • y
  • yes
  • true
  • on
  • 1

False values are as follows.

  • n
  • no
  • f
  • false
  • off
  • 0

If val is not any of the above, it raises ValueError.

9. API Reference – strtobool() — Python 3.10.0 Documentation

It is not case sensitive, so for example, you can use the following; any other string will result in an error.

  • 'TRUE'
  • 'True'
  • 'YES'
from distutils.util import strtobool

print(strtobool('true'))
print(strtobool('True'))
print(strtobool('TRUE'))
# 1
# 1
# 1

print(strtobool('t'))
print(strtobool('yes'))
print(strtobool('y'))
print(strtobool('on'))
print(strtobool('1'))
# 1
# 1
# 1
# 1
# 1

print(strtobool('false'))
print(strtobool('False'))
print(strtobool('FALSE'))
# 0
# 0
# 0

print(strtobool('f'))
print(strtobool('no'))
print(strtobool('n'))
print(strtobool('off'))
print(strtobool('0'))
# 0
# 0
# 0
# 0
# 0

# print(strtobool('abc'))
# ValueError: invalid truth value 'abc'

The name is strtobool(), but the return value is not bool, but int (1 or 0).

print(type(strtobool('true')))
# <class 'int'>

As written earlier, when type=xxx is specified in add_argument() of argparse, the argument will be passed to xxx(). Therefore, we can do the following.
type=strtobool

import argparse
from distutils.util import strtobool

parser = argparse.ArgumentParser()
parser.add_argument('arg_bool', type=strtobool)

args = parser.parse_args()
print(args.arg_bool)
print(type(args.arg_bool))

The return value is not a bool type, but an int type 1 or 0, but it can read true or false values with true or false as arguments.

$ python argparse_type_strtobool.py true
1
<type 'int'>

$ python argparse_type_strtobool.py false
0
<type 'int'>

Also, if the argument is not expected, an error will be generated properly.

$ python argparse_type_strtobool.py bar
usage: argparse_type_strtobool.py [-h] arg_bool
argparse_type_strtobool.py: error: argument arg_bool: invalid strtobool value: 'bar'