Validating at parse time gives users immediate, specific errors instead of failures deep in your code.
type= accepts any callable taking one string. That makes it the natural place for validation and conversion beyond int and float:
def port(s):
n = int(s)
if not 1 <= n <= 65535:
raise argparse.ArgumentTypeError(f"{n} is not a valid port")
return nHow errors are reported:
argparse.ArgumentTypeError("msg"): your message is shown verbatim:argument --port: 70000 is not a valid port.ValueErrororTypeError: a generic message using the function's name:invalid port value: 'x'. Name your functions well.
Good candidates: positive integers, durations like "5m", comma-separated lists, datetime.date.fromisoformat, key=value pairs, existing paths. Standard-library constructors often work directly: type=date.fromisoformat, type=Decimal.
Keep type functions pure: don't open network connections or files at parse time. Checking that a path exists is fine, but opening it is better left to your main code inside a with block.
The choices check runs after type, so a custom type that normalises case (str.lower) pairs nicely with lowercase choices.
Example
import argparse
from datetime import date
def positive_int(s):
n = int(s) # ValueError -> generic message
if n <= 0:
raise argparse.ArgumentTypeError(f"must be > 0, got {n}")
return n
def csv_list(s):
return [part.strip() for part in s.split(",") if part.strip()]
p = argparse.ArgumentParser(prog="report")
p.add_argument("--since", type=date.fromisoformat)
p.add_argument("--top", type=positive_int, default=10)
p.add_argument("--tags", type=csv_list, default=[])
p.add_argument("--fmt", type=str.lower, choices=["md", "html"], default="md")
a = p.parse_args(["--since", "2026-01-31", "--tags", "a, b,,c", "--fmt", "HTML"])
print(a.since.month, a.top, a.tags, a.fmt) # 1 10 ['a', 'b', 'c'] html
for bad in (["--top", "0"], ["--top", "x"]):
try: p.parse_args(bad)
except SystemExit: pass
# report: error: argument --top: must be > 0, got 0
# report: error: argument --top: invalid positive_int value: 'x'Remember
- type= takes any str -> value callable
- ArgumentTypeError shows your message; ValueError shows a generic one
- type runs before choices, so normalise there
Watch out for
- Opening files or network resources inside a type function, leaking them if parsing later fails.