PythonBuilding CLI Apps (argparse)

Building CLI Apps (argparse)

Almost every useful script eventually needs to accept input from the command line — a filename, a flag to switch behaviour on, a count, a mode. Python's standard library ships with argparse, a module built specifically for this: it parses sys.argv, validates the values, converts types, and generates helpful --help output for free. You don't need to install anything to use it.

Why not just read sys.argv directly?

You could index into sys.argv manually, but you'd quickly end up re-implementing type conversion, error messages for missing arguments, and usage text yourself. argparse does all of that consistently, and the resulting CLI behaves the way users expect from any well-built command-line tool.

The basic pattern

Every argparse program follows the same three steps: create a parser, describe the arguments it accepts, then parse sys.argv into a namespace object.

greet.py

Python
import argparse

parser = argparse.ArgumentParser(
    prog="greet",
    description="Greet someone from the command line.",
)

# Positional argument — required, order matters
parser.add_argument("name", help="Name of the person to greet")

# Optional argument with a default value
parser.add_argument(
    "-g", "--greeting",
    default="Hello",
    help="Greeting word to use (default: Hello)",
)

# Boolean flag — no value, just present or absent
parser.add_argument(
    "-s", "--shout",
    action="store_true",
    help="Print the greeting in uppercase",
)

args = parser.parse_args()

message = f"{args.greeting}, {args.name}!"
if args.shout:
    message = message.upper()

print(message)

Run it like a normal command-line tool:

Bash
python greet.py Ada
python greet.py Ada --greeting "Good morning"
python greet.py Ada -g Hi -s
Hello, Ada!
Good morning, Ada!
HI, ADA!
Positional vs optional arguments
  • Positional arguments (name) are required and matched by position — no dashes in their name.

  • Optional arguments (-g/--greeting) start with - or -- and can appear in any order, or be omitted if a default is set.

  • Boolean flags use action='store_true' — the value is True if the flag is present on the command line, False otherwise. There is nothing to type after the flag.

  • Use type=int, type=float, etc. on add_argument() to have argparse convert and validate the value for you, raising a clean error if conversion fails.

  • Use choices=[...] to restrict an argument to a fixed set of valid values.

Help text comes for free

Every parser automatically supports -h / --help, built entirely from the description and per-argument help strings you already wrote:

Bash
python greet.py --help
usage: greet [-h] [-g GREETING] [-s] name

Greet someone from the command line.

positional arguments:
  name                  Name of the person to greet

options:
  -h, --help            show this help message and exit
  -g GREETING, --greeting GREETING
                        Greeting word to use (default: Hello)
  -s, --shout           Print the greeting in uppercase
This is not optional polish
Users will run `--help` before reading any documentation you write. Investing a sentence or two in each `help=` string pays off immediately.
A slightly bigger example: a file-processing tool

Real CLI tools usually combine several arguments together — something to act on, a mode, and a couple of switches:

wordcount.py

Python
import argparse
from pathlib import Path

parser = argparse.ArgumentParser(description="Count words in a text file.")
parser.add_argument("path", type=Path, help="Path to the text file")
parser.add_argument(
    "-n", "--top",
    type=int,
    default=5,
    help="Number of most common words to show (default: 5)",
)
parser.add_argument(
    "--ignore-case",
    action="store_true",
    help="Treat words as case-insensitive",
)

args = parser.parse_args()

text = args.path.read_text(encoding="utf-8")
if args.ignore_case:
    text = text.lower()

words = text.split()
print(f"Total words: {len(words)}")

from collections import Counter
for word, count in Counter(words).most_common(args.top):
    print(f"{word}: {count}")
When you outgrow argparse

argparse is more than enough for small-to-medium scripts, and being built into the standard library means zero extra dependencies. Once a CLI grows into multiple subcommands, shell auto-completion, or richer type-based validation, two third-party libraries are worth knowing about:

  • click — declares commands and options with decorators (@click.command(), @click.option()), and has excellent support for nested subcommands and testing.

  • typer — built on top of click, lets you declare arguments as plain Python type-hinted function parameters and generates the CLI (including shell auto-completion) from them automatically.

Note
Both are installed with `pip install click` or `pip install typer`. For a single-file script with a handful of arguments, though, `argparse` alone is usually the simplest choice — it's already there.