Odd inputs (values starting with '-', abbreviations, greedy nargs) cause the most confusing CLI bugs.
argparse first classifies each word as an option (O) or argument (A), then matches patterns of these against your arguments using regular expressions over the O/A string. Several rules follow from that:
- Prefix matching:
--verbis accepted for--verboseif unambiguous. That can break scripts when you later add--verbatim. Disable withallow_abbrev=False. - Negative numbers:
-5is treated as a value, not an option, unless the parser has options that look like negative numbers (such as-1). Then users must write--offset=-5. - The `--` separator: everything after
--is positional, even if it starts with a dash, sorm -- -weird-fileworks. - Values starting with `-`:
--pattern -xfails because-xlooks like an option; use--pattern=-x. - Greedy nargs: positionals are matched greedily left to right, so
nargs="*"followed by another positional can steal values. - Intermixing: by default, positionals with
nargsmust be contiguous;parse_intermixed_args()allows options in between.
When in doubt, print parser.parse_args(argv) for a list of tricky inputs and add them to your tests.
Example
import argparse
p = argparse.ArgumentParser(prog="calc")
p.add_argument("--verbose", action="store_true")
p.add_argument("--offset", type=int, default=0)
p.add_argument("--pattern")
p.add_argument("files", nargs="*")
print(p.parse_args(["--verb", "--offset", "-5"])) # prefix match; -5 is a value
# Namespace(verbose=True, offset=-5, pattern=None, files=[])
print(p.parse_args(["--pattern=-x", "--", "-file.txt"]).files) # ['-file.txt']
strict = argparse.ArgumentParser(prog="strict", allow_abbrev=False)
strict.add_argument("--verbose", action="store_true")
try:
strict.parse_args(["--verb"])
except SystemExit:
pass
# strict: error: unrecognized arguments: --verb
q = argparse.ArgumentParser()
q.add_argument("cmd"); q.add_argument("--n"); q.add_argument("rest", nargs="*")
print(q.parse_intermixed_args(["go", "a", "--n", "1", "b"]))
# Namespace(cmd='go', n='1', rest=['a', 'b'])Remember
- Long options can be abbreviated unless allow_abbrev=False
- Use --opt=-value or -- for values that start with a dash
- parse_intermixed_args allows options between positional values
Watch out for
- Shipping a CLI with prefix matching on, then breaking users' abbreviations by adding a new option.