Code Reference

CLI Tool

Python · Example / how-to

CLI Tool

Python · Example / how-to


📋 Overview

Build a small command-line tool with argparse, structured logging, and an entrypoint under if __name__ == "__main__". Package it later with a console script in pyproject.toml.

🔧 Core concepts

PieceRole
ParserFlags, args, subcommands
Exit codes0 ok, non-zero failure
Logging-v / -q adjust level
PathFile arguments
main()Testable entry

Keep business logic in functions; CLI layer only parses and prints.

💡 Examples

greet.py:

from __future__ import annotations

import argparse
import logging
import sys
from pathlib import Path

log = logging.getLogger("greet")

def greet(name: str, shout: bool = False) -> str:
    msg = f"Hello, {name}!"
    return msg.upper() if shout else msg

def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(description="Greet someone")
    p.add_argument("name", help="person to greet")
    p.add_argument("-s", "--shout", action="store_true")
    p.add_argument("-o", "--output", type=Path)
    p.add_argument("-v", "--verbose", action="count", default=0)
    return p

def main(argv: list[str] | None = None) -> int:
    args = build_parser().parse_args(argv)
    level = logging.WARNING - (10 * min(args.verbose, 2))
    logging.basicConfig(level=level, format="%(levelname)s %(message)s")

    message = greet(args.name, shout=args.shout)
    log.info("writing greeting")
    if args.output:
        args.output.write_text(message + "\n", encoding="utf-8")
    else:
        print(message)
    return 0

if __name__ == "__main__":
    sys.exit(main())

Usage:

python greet.py Ada
python greet.py Ada --shout -v -o out.txt

Test main without subprocess:

def test_main_stdout(capsys):
    assert main(["Ada"]) == 0
    assert "Hello, Ada!" in capsys.readouterr().out

⚠️ Pitfalls

  • Parsing at import time breaks tests and packaging.
  • Printing errors to stdout — use stderr + non-zero exit.
  • Relative paths depend on cwd — document or resolve carefully.
  • Catching all exceptions without logging hides failures.
  • For richer CLIs, consider Typer/Click once argparse gets painful.

On this page