Configs2026年4月14日·1 分钟阅读

Typer — Build Python CLIs from Type Hints

Typer turns Python functions into CLI commands using only type hints. It is built on Click but removes the boilerplate — write a function, and Typer auto-generates parsing, help text, and shell completions.

Introduction

Typer is the "FastAPI of CLIs" — same author (Sebastián Ramírez), same type-hint-first philosophy. It wraps Click but lets you declare options and arguments just by writing a Python function signature. Your type hints become validation, and your docstrings become help text.

With over 15,000 GitHub stars, Typer is used by dbt Cloud CLI, typer itself, and thousands of tools that want modern Python CLIs without Click boilerplate.

What Typer Does

Typer reads your function signature: parameters without defaults become required arguments, parameters with defaults become options, bool params become flags, and types (int, float, Path, Enum) become validators. It generates --help, shell completion, and nested subcommands automatically.

Architecture Overview

@app.command()
def fn(a: int, b: str = "x", flag: bool = False): ...
        |
   [Typer Introspection]
   Read signature + type hints
        |
   [Click Command Tree] (under the hood)
        |
   [Argv Parser]
   Validation + type coercion
        |
   [Your function called]
        |
   [Rich output]  (auto-wired for pretty errors)

Self-Hosting & Configuration

import typer
from typing import Optional
from pathlib import Path
from enum import Enum

app = typer.Typer(help="Awesome CLI")
users_app = typer.Typer(help="Manage users")
app.add_typer(users_app, name="users")

class Format(str, Enum):
    json = "json"
    yaml = "yaml"

@app.command()
def export(
    out: Path = typer.Argument(..., help="Output file"),
    fmt: Format = Format.json,
    verbose: bool = typer.Option(False, "--verbose", "-v"),
):
    """Export data to OUT in the chosen format."""
    if verbose:
        typer.secho(f"Exporting to {out} as {fmt.value}", fg="green")
    out.write_text("...")

@users_app.command("add")
def users_add(name: str, admin: bool = False):
    typer.echo(f"Added {name} (admin={admin})")

if __name__ == "__main__":
    app()

Key Features

  • Type hints become parsers — int, float, Path, bool, Enum, Optional
  • Subcommands — nest Typer apps for git-style command trees
  • Auto-generated help — from docstrings and parameter metadata
  • Shell completions--install-completion for bash/zsh/fish/PowerShell
  • Promptingtyper.prompt() and typer.confirm() for interactive input
  • Rich integration — colorful tracebacks and prompts
  • TestableCliRunner from Click works unchanged
  • FastAPI DX — same ergonomics for developers who love FastAPI

Comparison with Similar Tools

Feature Typer Click argparse Fire docopt
Type hints drive parsing Yes No (decorators) No Partial No (docstring)
Subcommands Yes Yes Nested Yes Limited
Auto completion Yes Yes No No No
Learning Curve Very Low Low Moderate Very Low Moderate
Built on Click Standalone stdlib Inspect Docstring DSL
Best For Modern Python Any Python CLI Stdlib only Quick scripts Declarative

FAQ

Q: Typer vs Click — do I need both? A: Typer is a layer on top of Click. Use Typer for new projects; drop down to Click features when you need advanced custom types or parsers.

Q: How do I test a Typer app? A: from typer.testing import CliRunner. Invoke with args and check result.exit_code and result.stdout.

Q: Does Typer support async commands? A: Not directly — Typer invokes sync functions. Use asyncio.run(...) inside your command, or wrap with a small helper.

Q: Can I use Typer without global app state? A: Yes. You can use @typer.run on a single function for one-off scripts, no Typer() instance needed.

Sources

讨论

登录后参与讨论。
还没有评论,来写第一条吧。

相关资产