CLI Basics
10 examples to get you started with CLI Tools - 7 basic and 3 intermediate.
Prerequisites
- Python 3.14.0
- Optional:
uv add click typer richfor intermediate examples
Basic Examples
1. sys.argv Basics
The simplest CLI reads arguments from sys.argv.
import sys
if len(sys.argv) < 2:
print("Usage: greet.py NAME")
sys.exit(1)
print(f"Hello, {sys.argv[1]}!")sys.argv[0]is the script name- Exit code 1 signals error to shell scripts
- No dependencies needed
- Insufficient for real tools - use argparse
Related: argparse - proper argument parsing
2. argparse Essentials
stdlib argument parsing with auto-generated help.
import argparse
parser = argparse.ArgumentParser(description="Greet users")
parser.add_argument("name", help="Name to greet")
parser.add_argument("--loud", action="store_true", help="Uppercase output")
args = parser.parse_args()
msg = f"Hello, {args.name}!"
print(msg.upper() if args.loud else msg)--helpgenerated automaticallyaction="store_true"for boolean flags- Positional and optional arguments supported
- Standard library - no install needed
Related: argparse - full guide
3. Exit Codes
Return meaningful exit codes for shell integration.
import sys
def main() -> int:
try:
run()
return 0
except FileNotFoundError:
print("File not found", file=sys.stderr)
return 2
except PermissionError:
return 13
sys.exit(main())- 0 = success, non-zero = failure
- Use stderr for error messages
- Document exit codes in
--help - CI and shell scripts check
$?
4. stdin/stdout/stderr
CLIs read from stdin and write to stdout; errors go to stderr.
import sys
for line in sys.stdin:
print(line.strip().upper())
# Usage: cat file.txt | python upper.py > out.txt- stdout for data output (pipeable)
- stderr for logs and errors (not piped)
- Enables composition:
cmd1 | cmd2 --verboselogs should go to stderr
5. Subcommands
Organize CLI into verbs: git commit, docker run.
import argparse
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(dest="command", required=True)
add = sub.add_parser("add", help="Add item")
add.add_argument("name")
remove = sub.add_parser("remove", help="Remove item")
remove.add_argument("id", type=int)
args = parser.parse_args()add_subparserscreates command groups- Each subparser has its own arguments
dest="command"identifies which subcommand ran- Foundation for multi-command tools
Related: Click - decorator-based subcommands
6. Type Conversion
argparse converts string args to typed values.
parser.add_argument("--port", type=int, default=8000)
parser.add_argument("--ratio", type=float, default=0.5)
parser.add_argument("--tags", nargs="+") # one or more stringstype=intvalidates and convertsnargs="+"collects multiple valueschoices=[...]restricts valid values- Invalid types produce clear error messages
7. Environment Variables
Read config from env vars as fallback.
import os
api_key = os.environ.get("API_KEY")
if not api_key:
raise SystemExit("Set API_KEY environment variable")- 12-factor apps use env vars for config
- Flag overrides env var is the common precedence
- Never print secrets to stdout
- Document required env vars in
--help
Related: Config & Environment - layering config
Intermediate Examples
8. Click for Ergonomics
Click uses decorators for cleaner CLI code.
import click
@click.command()
@click.option("--count", default=1, help="Number of greetings")
@click.argument("name")
def greet(name, count):
for _ in range(count):
click.echo(f"Hello, {name}!")
if __name__ == "__main__":
greet()- Decorators replace argparse boilerplate
click.echohandles encoding correctly@click.group()for subcommands- Colored output and prompts built in
Related: Click - full Click guide
9. Rich for Output
Formatted tables, progress bars, and syntax highlighting.
from rich.console import Console
from rich.table import Table
console = Console()
table = Table(title="Users")
table.add_column("ID", style="cyan")
table.add_column("Email")
table.add_row("1", "ada@example.com")
console.print(table)- Tables, panels, and progress bars
- Automatic terminal width detection
- Fallback gracefully on dumb terminals
- Use for human-facing output; plain text for machine parsing
Related: Rich Output - formatting guide
10. Package and Distribute
Ship CLI via entry points and pipx.
[project.scripts]
mytool = "mytool.cli:main"pipx install mytool
mytool --help[project.scripts]registers command on install- pipx isolates CLI in its own venv
- Users get a global command without polluting system Python
- See packaging section for full workflow
Related: Packaging & Distributing CLIs
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.