ProvenPin Install

Typer CLI with subcommands, options and CliRunner tests: working versions (tested 2026-10-15)

Build a Typer CLI with subcommands, typed options, env var fallbacks and exit codes, tested with typer.testing.CliRunner.

passed4 passed · 0 failed2026-10-15 11:57 UTC
Status
passed
Last passed
2026-10-15 11:57 UTC
Tests
4 / 4
Language
Python
Runtime
Python 3.13.16
pinned versions2
typer@0.27.3pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38730348195

The code

Exactly the files that passed. Your agent can pull them with get_recipe("typer-cli-testing") (install), or see them on GitHub.

requirements.txt
typer==0.27.3
pytest==9.1.1
cli.py
from typing import Annotated
import typer

app = typer.Typer(no_args_is_help=True)
users = typer.Typer(help="Manage users")
app.add_typer(users, name="users")
DB: dict[str, int] = {}

@users.command("add")
def add_user(name: str, age: Annotated[int, typer.Option(min=0, max=150)] = 0):
    if name in DB:
        typer.echo(f"{name} already exists", err=True)
        raise typer.Exit(code=1)
    DB[name] = age
    typer.echo(f"added {name} ({age})")

@users.command("list")
def list_users():
    for n, a in sorted(DB.items()):
        typer.echo(f"{n}\t{a}")

@app.command()
def greet(name: Annotated[str, typer.Option(envvar="GREET_NAME")] = "world", shout: bool = False):
    msg = f"hello {name}"
    typer.echo(msg.upper() if shout else msg)

if __name__ == "__main__":
    app()
tests/test_cli.py
from typer.testing import CliRunner
from cli import app, DB

runner = CliRunner()

def setup_function():
    DB.clear()

def test_add_and_list():
    assert runner.invoke(app, ["users", "add", "ada", "--age", "36"]).exit_code == 0
    r = runner.invoke(app, ["users", "list"])
    assert "ada\t36" in r.output

def test_duplicate_exits_1():
    runner.invoke(app, ["users", "add", "ada"])
    assert runner.invoke(app, ["users", "add", "ada"]).exit_code == 1

def test_option_range_validation():
    assert runner.invoke(app, ["users", "add", "bo", "--age", "200"]).exit_code == 2

def test_envvar_and_flag():
    r = runner.invoke(app, ["greet", "--shout"], env={"GREET_NAME": "jp"})
    assert r.output.strip() == "HELLO JP"
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.