Skip to content

typer-template

A template for building Python CLI applications with Typer, featuring Rich output, structured logging via structlog, and environment-based configuration.

Stack

  • Typer — CLI framework
  • Rich — terminal output and progress bars
  • structlog — structured logging with context binding; dev console output or JSON for aggregators
  • Pydantic Settings — environment-based configuration
  • uv — package management
  • Ruff — linting and formatting
  • MyPy — strict type checking
  • pytest + pytest-xdist — parallel testing with coverage enforcement
  • commitizen — Conventional Commits + automated versioning and CHANGELOG
  • MkDocs Material — documentation site

Requirements

  • Python 3.13+
  • uv

Getting Started

# Clone the repo and install dependencies
uv sync --all-extras

# Run the CLI
uv run cli-app --help

# Install pre-commit hooks (enforces Conventional Commits)
uv run pre-commit install

Development

Tasks are available via taskipy — run with uv run task <name>:

uv run task lint        # ruff check
uv run task fmt         # ruff format
uv run task typecheck   # mypy src/
uv run task test        # pytest (parallel, 80% coverage enforced)
uv run task test-fast   # pytest --no-cov -n auto
uv run task audit       # pip-audit dependency audit

Or run the tools directly:

uv run pytest --no-cov -n auto          # fast parallel run
uv run pytest tests/path/to/test.py     # single file

Docs

uv run --group docs mkdocs serve      # live preview at http://127.0.0.1:8000
uv run --group docs mkdocs build      # build static site to site/
uv run --group docs mkdocs gh-deploy  # deploy to GitHub Pages (gh-pages branch)

Source is in docs/. Powered by MkDocs Material with mkdocstrings for API reference generation.

Global CLI Flags

The root app exposes flags available to every subcommand:

Flag Short Default Effect
--verbose -V off Sets log level to DEBUG at runtime
--output-format -f text text (Rich) or json (machine-readable)
--version -v Print version and dependency list, then exit
--authors -A Print author contacts, then exit
cli-app --verbose command example-command hello
cli-app --output-format json command example-command hello
echo "hello" | cli-app command example-command   # stdin piping

Shell Completion

cli-app completion install           # install for the current shell
cli-app completion install --shell zsh
cli-app completion show              # print the completion script

Or directly via the built-in Typer flags:

cli-app --install-completion
cli-app --show-completion

Versioning & Changelog

This template uses Conventional Commits enforced by a commit-msg pre-commit hook.

# Bump version, update CHANGELOG, tag
uv run cz bump

# Preview changelog without bumping
uv run cz changelog --dry-run

Bump type is inferred automatically: fix: → patch, feat: → minor, feat!: / BREAKING CHANGE → major.

Configuration

Console and logging behaviour can be configured via environment variables or a .env file:

Prefix Controls
CLI_APP_CONSOLE_* Rich console settings (theme, colors, width)
CLI_APP_LOG_* Log level, file rotation, JSON output
CLI_APP_LOG_LEVEL=DEBUG
CLI_APP_LOG_USE_JSON_FORMATTER=true   # emit JSON logs (for Datadog, Loki, etc.)
CLI_APP_CONSOLE_WIDTH=120

Project Structure

src/
└── cli_app/
    ├── main.py              # entry point
    ├── cli/
    │   ├── app.py           # root Typer app (--verbose, --output-format, --version, --authors)
    │   ├── callbacks/       # eager option callbacks (--version, --authors)
    │   └── commands/
    │       ├── command.py   # example command group (stdin + output-format patterns)
    │       └── completion.py # shell completion sub-app
    ├── core/                # domain logic + Settings
    └── utils/
        ├── console.py       # singleton Rich Console
        ├── log.py           # structlog setup (ConsoleRenderer dev / JSON prod)
        ├── output.py        # OutputFormat enum, render_output(), echo_json()
        ├── stdin.py         # is_stdin_piped(), read_stdin_if_piped(), iter_stdin_lines()
        ├── meta.py          # distribution metadata at runtime
        ├── format.py        # Rich Theme
        ├── emoji.py         # Emoji StrEnum
        ├── progress.py      # Rich Progress bar factory
        └── misc.py          # find_project_root()

CI/CD

.github/workflows/ci.yml runs on every push/PR to main and dev:

Step What it does
ruff format formatting check
ruff lint linting
mypy strict type checking
pytest parallel tests, 80% coverage enforced
pip-audit known CVE check for dependencies
trivy filesystem vulnerability scan (CRITICAL/HIGH, fails build)

Dependabot opens weekly PRs for both pip packages and GitHub Actions.

.github/workflows/release.yml runs on version tags (v*):

Job What it does
publish builds the package and publishes to PyPI via OIDC trusted publisher (no secrets)
docs deploys MkDocs to GitHub Pages

PyPI publishing uses keyless OIDC — configure a trusted publisher on PyPI pointing to this repo with workflow file release.yml.

Author

Igor Lashkov — rwrotson@yandex.ru