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=Falsesets 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 --verboseIf 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-verbose3) 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) -> FalseDrop-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 0Takeaway#
- 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.