Real tools combine CLI flags, config files and env vars without duplicating argument definitions.
Parent parsers share argument definitions. Build a parser with add_help=False holding common options, then pass parents=[common] to each parser or subparser that needs them. The arguments are copied in.
Argument files: ArgumentParser(fromfile_prefix_chars="@") lets users write tool @args.txt; each line of the file is read as one argument. Override convert_arg_line_to_args to split lines differently, for example by whitespace.
Layered configuration usually follows precedence: command line > environment > config file > built-in default. argparse has no built-in support, but set_defaults and get_default make it straightforward:
- parse once with only a
--configoption usingparse_known_args, - load the file (
tomllib,json) and the relevant environment variables, - call
parser.set_defaults(**loaded), which overrides the defaults declared inadd_argument, - parse the full command line; explicit flags win.
argparse.SUPPRESS as a parser's argument_default omits unspecified options from the Namespace entirely, which lets you tell "not given" from "given the default value" when merging layers yourself.
Example
import argparse, os
common = argparse.ArgumentParser(add_help=False)
common.add_argument("--region", default="us")
common.add_argument("--profile", default="default")
p = argparse.ArgumentParser(prog="cloud", parents=[common], fromfile_prefix_chars="@")
p.add_argument("--replicas", type=int, default=1)
with open("prod.args", "w") as f:
f.write("--region\neu\n--replicas\n5\n")
print(vars(p.parse_args(["@prod.args"])))
# {'region': 'eu', 'profile': 'default', 'replicas': 5}
os.environ["CLOUD_PROFILE"] = "ci"
file_config = {"replicas": 3, "region": "ap"} # e.g. from tomllib
p.set_defaults(**file_config, profile=os.environ.get("CLOUD_PROFILE", "default"))
print(vars(p.parse_args(["--region", "eu"])))
# {'region': 'eu', 'profile': 'ci', 'replicas': 3} CLI > env > fileRemember
- parents=[common] copies shared arguments into several parsers
- fromfile_prefix_chars='@' reads arguments from files
- set_defaults() lets config files and env vars supply defaults the CLI can override
Watch out for
- Forgetting add_help=False on a parent parser, causing a conflicting -h option.