argparser
- 2 mins read
Booleans in argparse (and how to fix it)
TL;DR: Don’t use type=bool for CLI flags. It turns strings like "False" into True. Use actions instead: action='store_true', action='store_false', or (Python ≥ 3.9) BooleanOptionalAction.
Why --verbose=False becomes True
argparse reads CLI values as strings. With type=bool, it effectively does:
bool("False") # -> True (any non-empty string is truthy)
So running:
python app.py --verbose=False
sets args.verbose to True — surprise!
Correct patterns
1) One-way flags (present = True, absent = False)
parser.add_argument('--verbose', action='store_true', help='enable verbose logging')
# default is False unless you pass --verbose
If you want the inverse (default True, disable with a flag):
parser.add_argument('--no-cache', action='store_false', dest='cache', help='disable cache')
parser.set_defaults(cache=True)
2) Two-way flags (Python ≥ 3.9)
Gives you --feature and --no-feature.
from argparse import BooleanOptionalAction
parser.add_argument('--verbose', action=BooleanOptionalAction, default=False)
# supports: --verbose / --no-verbose
3) If you must accept --flag=True|False
Use a small parser that understands common boolean spellings:
def str2bool(v):
if isinstance(v, bool):
return v
v = v.lower()
if v in ('1','true','t','yes','y','on'):
return True
if v in ('0','false','f','no','n','off'):
return False
raise argparse.ArgumentTypeError("expected a boolean")
parser.add_argument('--verbose', type=str2bool, nargs='?', const=True, default=False)
# --verbose -> True
# --verbose true -> True
# --verbose false -> False
# (omitting it) -> False
Drop-in snippet with fallback (works on all Python versions)
import argparse
parser = argparse.ArgumentParser()
try:
# Python 3.9+
from argparse import BooleanOptionalAction
parser.add_argument('--verbose', action=BooleanOptionalAction, default=False,
help='enable or disable verbose logs')
except Exception:
# Older Pythons
parser.add_argument('--verbose', action='store_true', help='enable verbose logs')
# optional companion to explicitly disable (rarely needed):
parser.add_argument('--no-verbose', dest='verbose', action='store_false')
args = parser.parse_args()
Bonus: converting to libraries that expect ints
Some libs (e.g., Stable-Baselines3) take verbose as an int. Just map:
sb3_verbose = 1 if args.verbose else 0
Takeaway
- Avoid
type=boolinargparse. - Prefer actions (
store_true,store_false,BooleanOptionalAction). - If you need
--flag=True|False, supply astr2boolparser.
Copy-paste this into your PR to save future-you (and reviewers) from confusing CLI behavior.