The argparse module builds small and medium command-line programs without third-party dependencies. The sections below start from a runnable greeting script, then add positional parameters, optional flags, types, nargs, and subcommands in a typical learning order for turning a script into a CLI.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
What is argparse in Python?
The official documentation describes argparse as the standard library module for command-line interfaces: programs define arguments, argparse reads sys.argv, prints help for -h / --help, and reports invalid input. In code, an ArgumentParser is configured with add_argument, then parse_args() turns the remaining tokens into attributes on a Namespace object (optionally applying type and choices).
Basic argparse example
Save the following as app.py and run it with a name on the command line. The positional name is required; -v / --verbose is an optional switch.
import argparse
parser = argparse.ArgumentParser(description="Greet a user")
parser.add_argument("name", help="Name of the user")
parser.add_argument("-v", "--verbose", action="store_true", help="Show extra output")
args = parser.parse_args()
if args.verbose:
print(f"Hello, {args.name}. Verbose mode is enabled.")
else:
print(f"Hello, {args.name}.")Typical invocations:
python app.py Deepak
python app.py Deepak --verbose
python app.py --helpThe first line prints a short greeting. The second enables the verbose branch. --help lists name, -v, and the script description without running the rest of main logic beyond what parse_args needs for help.
Add positional arguments
Positional arguments appear in order after the script name and do not use - prefixes. Each parser.add_argument("label", ...) consumes the next token from the command line.
import argparse
parser = argparse.ArgumentParser(description="Copy-style two-argument CLI")
parser.add_argument("source", help="Input path")
parser.add_argument("destination", help="Output path")
args = parser.parse_args()
print(f"{args.source} -> {args.destination}")python copy_cli.py notes.txt notes.bakAdd optional arguments
Optional arguments use short (-s) and/or long (--long) flags. Values can sit in the next token or use --opt=value form for long options.
import argparse
parser = argparse.ArgumentParser(description="Optional greeting prefix")
parser.add_argument("name")
parser.add_argument("-t", "--title", default="Mr.", help="Honorific")
args = parser.parse_args()
print(f"Hello, {args.title} {args.name}")Add boolean flags with store_true
action="store_true" stores False until the flag appears, then sets the attribute to True. Pair with action="store_false" when the default should be True and the flag turns it off.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
parser.add_argument("--quiet", action="store_false", dest="verbose", help="Disable verbose")
parser.set_defaults(verbose=True)
args = parser.parse_args()
print(args.debug, args.verbose)Set default values for arguments
The default keyword sets the value when the flag or optional positional is omitted. Defaults apply after parsing, so application code can read args.field without extra None checks for every flag.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-n", "--count", type=int, default=1, help="Repeat count")
args = parser.parse_args()
print("tick" * args.count)Convert argument types with type
The type callable converts each incoming string before validation. Built-ins such as int and float are common; a small function can enforce extra rules by raising argparse.ArgumentTypeError on failure.
import argparse
def positive_int(text):
value = int(text)
if value <= 0:
raise argparse.ArgumentTypeError("expected a positive integer")
return value
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--port", type=positive_int, default=8080)
args = parser.parse_args()
print(args.port)Restrict values with choices
choices limits input to a fixed sequence or set of allowed strings. Invalid values produce a clear parser error before application logic runs.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--level", choices=("DEBUG", "INFO", "WARNING"), default="INFO")
args = parser.parse_args()
print(args.level)Accept multiple values with nargs
nargs controls how many tokens one definition consumes: ? (zero or one), * (zero or more), + (one or more), or an integer for an exact count. Combined with type=int, lists of numbers parse cleanly.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--tags", nargs="+", help="One or more tags")
parser.add_argument("--coords", nargs=3, type=int, metavar=("X", "Y", "Z"))
args = parser.parse_args()
print(args.tags, args.coords)Required vs optional arguments
Positional parameters are required unless nargs="?" (or * / +) and a default make them optional. For long/short options, pass required=True when the option string must appear even though it looks like a traditional optional flag.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--config", required=True, help="Path to config file")
parser.add_argument("input", nargs="?", default="-", help="File or - for stdin")
args = parser.parse_args()
print(args.config, args.input)Customize help text and usage message
ArgumentParser(description=...) sets the text shown below usage: in --help. epilog= adds a footer after the options list. prog= overrides the program name in usage lines. formatter_class=argparse.ArgumentDefaultsHelpFormatter appends default values into help strings automatically. metavar renames placeholders in usage without changing the dest attribute name.
import argparse
parser = argparse.ArgumentParser(
description="Demo help customization.",
epilog="Example: python demo.py --alpha 2",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument("--alpha", type=int, default=1, metavar="N", help="Exponent base")
args = parser.parse_args()
print(10 ** args.alpha)Access parsed arguments with args
parse_args() returns a argparse.Namespace. Attributes match the dest name (usually derived from the longest flag or the positional name). vars(args) copies fields into a dict for logging or serialization. Passing a list of strings into parse_args, as below, mirrors sys.argv pieces and is common in tests instead of launching a subprocess.
import argparse
import json
parser = argparse.ArgumentParser()
parser.add_argument("--name", dest="user")
args = parser.parse_args(["--name", "Ada"])
print(args.user)
print(json.dumps(vars(args)))Create subcommands with argparse
add_subparsers() attaches child parsers. Each child from add_parser("name", ...) receives its own arguments. A common pattern stores a handler on set_defaults(func=...) and calls it after parse_args() if present.
import argparse
def cmd_list(args):
print("listing…")
def cmd_add(args):
print(f"adding {args.item!r}")
parser = argparse.ArgumentParser(prog="tool")
sub = parser.add_subparsers(dest="command", required=True)
p_list = sub.add_parser("list", help="List items")
p_list.set_defaults(func=cmd_list)
p_add = sub.add_parser("add", help="Add an item")
p_add.add_argument("item")
p_add.set_defaults(func=cmd_add)
args = parser.parse_args()
args.func(args)python tool.py list
python tool.py add "new item"Handle argparse errors and validation
Custom type= functions should raise argparse.ArgumentTypeError with a short message. After parsing, call parser.error("message") to print usage and exit with status 2. For tests or libraries, parser.parse_args([]) can be wrapped to catch SystemExit when invalid input triggers the default error path. parse_known_args() returns (namespace, remaining_tokens) when unknown flags must be forwarded to another program.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--ratio", type=float, required=True)
args = parser.parse_args()
if not 0 <= args.ratio <= 1:
parser.error("--ratio must be between 0 and 1")
print("ratio ok:", args.ratio)python ratio_check.py --ratio 0.5
python ratio_check.py --ratio 2--ratio 0.5 prints ratio ok: 0.5. --ratio 2 fails after parsing: argparse prints the error string and exits with status 2.
Common argparse examples
These patterns recur in data scripts and small tools. Each subsection states the intent, shows a minimal parser, and lists example invocations so the wiring from sys.argv to behavior stays obvious.
File input argument
A single positional path is the simplest “run this on a file” interface. The path string is available as args.path; opening the file belongs in normal code after parsing (with try / except OSError as needed). Validate inputs with check if a file exists when the CLI must fail fast on missing paths.
import argparse
parser = argparse.ArgumentParser(description="Read a file path from the CLI")
parser.add_argument("path", help="File to process")
args = parser.parse_args()
print(f"Would open {args.path!r}")python ingest.py data.csvThe program receives data.csv as args.path. Omitting the path triggers argparse’s usage message and a non-zero exit.
Verbose mode flag
action="count" increments each time -v appears, which maps well to logging verbosity (0 = default, 1 = -v, 2 = -vv, and so on). The snippet maps a non-zero count to logging.DEBUG so diagnostic lines actually emit. See Python logging for handlers, formatters, and file output beyond basicConfig.
import argparse
import logging
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", action="count", default=0, help="Increase logging (-v -v)")
args = parser.parse_args()
level = logging.DEBUG if args.verbose else logging.WARNING
logging.basicConfig(level=level)
logging.getLogger(__name__).debug("debug enabled" if args.verbose else "quiet")python report.py
python report.py -vWith no flags, the root logger stays at WARNING and the .debug line is suppressed. With -v, the level drops to DEBUG and the debug message appears on stderr.
List of numbers argument
nargs="+" with type=int collects one or more integers into a list attribute. That matches “sum these numbers from the shell” without manual splitting of argv.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("values", nargs="+", type=int, help="Integers to sum")
args = parser.parse_args()
print(sum(args.values))python sumargs.py 2 4 6args.values becomes [2, 4, 6] and the script prints 12. Non-integer tokens raise a parser error before sum runs.
Choice-based argument
choices keeps output format (or environment name, log level string, and so on) aligned with a fixed set the rest of the code can branch on safely. The default applies when the flag is omitted.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--fmt", choices=("json", "yaml", "table"), default="json")
args = parser.parse_args()
print(f"output format: {args.fmt}")python emit.py
python emit.py --fmt yamlThe first run prints output format: json. The second prints output format: yaml. Values outside the tuple produce a standard argparse error listing allowed choices.
Common mistakes with argparse
- Treating optional flags as positional: strings starting with
-must be registered as optional arguments, not bare positionals. - Forgetting
required=Trueon an option that must always be supplied; without it, missing flags becomeNoneor thedefault. - Using
type=functions that raiseValueErrorinstead ofargparse.ArgumentTypeError, which produces less consistent error text. - Combining
nargs="*"withdefault=[]: mutable defaults are shared across parses; preferdefault=Noneand normalize to a list in code, or usedefault_factorypatterns outside argparse. - Calling
parse_args()in library code without guarding side effects: help and errors write to stderr and callsys.exit; tests often pass an explicit argv list as the first argument toparse_args, or useArgumentParser(exit_on_error=False)on Python 3.9+ when errors must not terminate the interpreter.
Python argparse quick reference table
| Goal | Typical add_argument usage |
|---|---|
| Required positional | parser.add_argument("name") |
| Optional positional | parser.add_argument("name", nargs="?", default="x") |
| Boolean flag | action="store_true" / action="store_false" |
| Default value | default=... |
| Typed value | type=int, custom callable |
| Allowed strings | choices=[...] |
| Several tokens | nargs="+", type=int |
| Required option | parser.add_argument("--id", required=True) |
| Subcommands | sub = parser.add_subparsers(...); sub.add_parser("name") |
Summary
The article walked from a minimal ArgumentParser through positional and optional parameters, defaults, type conversion, choices, nargs, required options, help customization, reading the Namespace, and subcommand dispatch. Validation belongs in type= hooks with ArgumentTypeError, or after parsing via parser.error. For scripts that shell out to other programs, combine parsed paths with Python subprocess and list-style argv. For larger CLIs, third-party libraries add layers on top of the same ideas, but argparse alone stays sufficient for many automation and tooling scripts.
References
- argparse — Parser for command-line options, arguments and sub-commands (Python standard library)

