The script everyone starts with

A script that takes a file and does something with it usually starts life like this:

import sys
input_file = sys.argv[1]

It works, until someone runs the script with no arguments, or the wrong number of them, or asks what arguments it even takes. sys.argv gives you the raw list of whatever was typed and nothing else: no validation, no help text, no error message better than a raw IndexError. argparse, in the standard library, handles all of that for a real, working CLI with a small amount of setup.

A real, small tool

import argparse

parser = argparse.ArgumentParser(description="Generate a summary report from a data file.")
parser.add_argument("input_file", help="path to the source data file")
parser.add_argument("--format", choices=["csv", "json", "html"], default="csv", help="output format")
parser.add_argument("--verbose", action="store_true", help="print progress while running")

args = parser.parse_args()
print(f"processing {args.input_file!r} -> {args.format} (verbose={args.verbose})")

One required positional argument, one optional flag with a fixed set of valid values, one boolean switch. Four add_argument() calls, and argparse already knows enough to do real work on its own.

What happens without touching the required argument

$ python report_cli.py
usage: report_cli.py [-h] [--format {csv,json,html}] [--verbose] input_file
report_cli.py: error: the following arguments are required: input_file

Exit code 2. Not a Python traceback, not an IndexError from reaching past the end of sys.argv, a real, purpose-built error message naming exactly which argument is missing, generated entirely from the add_argument() call above. Nothing in the script's own body ever ran.

A normal, successful run

$ python report_cli.py sales.csv --format json --verbose
processing 'sales.csv' -> json (verbose=True)

args.format and args.verbose come back as the right Python types already, a string and a real boolean, not raw text that needs parsing by hand.

An invalid choice, caught before the script ever runs

$ python report_cli.py sales.csv --format xml
usage: report_cli.py [-h] [--format {csv,json,html}] [--verbose] input_file
report_cli.py: error: argument --format: invalid choice: 'xml' (choose from csv, json, html)

choices=["csv", "json", "html"] is the entire validation rule. No if args.format not in (...) check anywhere in the script, and the resulting error message is more specific than most people would bother writing by hand, naming both what was given and what would have been accepted.

Help text, for free

$ python report_cli.py --help
usage: report_cli.py [-h] [--format {csv,json,html}] [--verbose] input_file

Generate a summary report from a data file.

positional arguments:
  input_file            path to the source data file

options:
  -h, --help            show this help message and exit
  --format {csv,json,html}
                        output format
  --verbose             print progress while running

Every help= string passed to add_argument() shows up here, auto-formatted, alongside a -h/--help flag that was never explicitly added. There's no separate documentation to keep in sync with the code; the parser definition is the documentation.

The takeaway

A missing argument, an invalid choice, and a request for help are three of the most common ways a command-line tool gets used wrong, and argparse handles all three from the same handful of add_argument() calls that already exist for real, functional reasons. The alternative, hand-rolling sys.argv parsing with a pile of if statements, ends up reimplementing a worse version of this for free.