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
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:
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 adefaultis set.Boolean flags use
action='store_true'— the value isTrueif the flag is present on the command line,Falseotherwise. There is nothing to type after the flag.Use
type=int,type=float, etc. onadd_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:
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 uppercaseA 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
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 ofclick, lets you declare arguments as plain Python type-hinted function parameters and generates the CLI (including shell auto-completion) from them automatically.