choices and nargs

argparse · Practical · 9 min

These two options cover most validation and multi-value needs without custom code.

choices= limits an argument to a fixed set: choices=["json", "csv"]. Invalid values get a clear error listing the options, and help shows them. Checking happens after type conversion, so type=int, choices=range(1, 6) works.

nargs= controls how many command-line words an argument consumes:

  • an integer N: exactly N values, as a list (nargs=2 for --size W H),
  • "?": zero or one. An option given without a value takes const; absent, it takes default,
  • "*": zero or more, as a list,
  • "+": one or more, error if none,
  • argparse.REMAINDER: everything that's left (rarely needed; prefer -- separation).

A common pattern is add_argument("files", nargs="+", type=Path) for "one or more files".

The ? + const combination gives three-state options:

p.add_argument("--log", nargs="?", const="app.log", default=None)
# absent -> None; --log -> 'app.log'; --log x.log -> 'x.log'

Be careful with several variable-length positionals in one parser: argparse matches greedily, so the split between them may not be what users expect.

Example

import argparse
from pathlib import Path

p = argparse.ArgumentParser(prog="conv")
p.add_argument("files", nargs="+", type=Path)
p.add_argument("--to", choices=["json", "csv"], default="json")
p.add_argument("--size", nargs=2, type=int, metavar=("W", "H"))
p.add_argument("--log", nargs="?", const="app.log")

a = p.parse_args(["a.txt", "b.txt", "--size", "640", "480", "--log"])
print([f.name for f in a.files], a.to, a.size, a.log)
# ['a.txt', 'b.txt'] json [640, 480] app.log
print(p.parse_args(["x", "--log", "debug.log"]).log)   # debug.log
try:
    p.parse_args(["x", "--to", "xml"])
except SystemExit:
    pass
# conv: error: argument --to: invalid choice: 'xml' (choose from 'json', 'csv')

Remember

  • choices restricts values and is checked after type conversion
  • nargs: N, '?', '*', '+' control how many values are consumed
  • nargs='?' with const gives absent / flag / value behaviour

Watch out for

  • Using nargs='*' for a required list; use '+' so an empty list is an error.

Check yourself

  1. add_argument('--log', nargs='?', const='a.log', default=None). What is args.log for the input '--log' alone?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/argparse.html#nargs