跳转至

Typer CLI 框架

一句话概述:Typer 是一个用于构建 CLI 应用的 Python 库,由 FastAPI 的作者开发。

核心知识点速查表

知识点说明
为什么要学 Typer见对应章节
核心概念见对应章节
安装配置见对应章节
快速上手见对应章节
进阶用法见对应章节
常见问题见对应章节
参考资源见对应章节

为什么要学 Typer

Typer 是一个用于构建 CLI 应用的 Python 库,由 FastAPI 的作者开发。它利用 Python 类型注解自动生成命令行参数解析、帮助文档和 Shell 自动补全。与 Click 和 argparse 相比,Typer 的代码更简洁、类型安全性更强,开发体验更接近现代 Python 风格。


核心概念

概念白话解释用途
App应用CLI 应用的主对象
Command命令子命令(如 git commit)
Argument参数必填的位置参数
Option选项可选的 --flag 参数
Callback回调应用级别的处理(全局选项)
Completion补全Shell Tab 补全

安装配置

pip install "typer[all]"  # 包含 rich 和 shellingham

快速上手

最简 CLI

import typer

def main(name: str):
    """向用户打招呼"""
    print(f"Hello {name}")

if __name__ == "__main__":
    typer.run(main)
python app.py World       # Hello World
python app.py --help       # 自动生成帮助文档

带选项的命令

import typer
from typing import Optional
from typing_extensions import Annotated

app = typer.Typer()

@app.command()
def greet(
    name: Annotated[str, typer.Argument(help="用户名")],
    greeting: Annotated[str, typer.Option(help="问候语")] = "Hello",
    count: Annotated[int, typer.Option("--count", "-c", help="重复次数")] = 1,
    uppercase: Annotated[bool, typer.Option(help="大写输出")] = False,
):
    """向用户打招呼"""
    message = f"{greeting} {name}"
    if uppercase:
        message = message.upper()
    for _ in range(count):
        typer.echo(message)

if __name__ == "__main__":
    app()
python app.py Alice                          # Hello Alice
python app.py Alice --greeting "你好" -c 3    # 你好 Alice (3次)
python app.py Alice --uppercase              # HELLO ALICE

子命令

import typer

app = typer.Typer(help="项目管理工具")

@app.command()
def init(name: str):
    """初始化项目"""
    typer.echo(f"初始化项目: {name}")

@app.command()
def build(release: bool = False):
    """构建项目"""
    mode = "release" if release else "debug"
    typer.echo(f"构建模式: {mode}")

@app.command()
def deploy(env: str = "staging"):
    """部署项目"""
    typer.echo(f"部署到: {env}")

if __name__ == "__main__":
    app()
python app.py init myproject
python app.py build --release
python app.py deploy --env production

进阶用法

交互式输入

@app.command()
def configure():
    name = typer.prompt("项目名称")
    password = typer.prompt("密码", hide_input=True)
    confirm = typer.confirm("确认创建?")

    if confirm:
        typer.echo(f"创建项目 {name}")
    else:
        typer.echo("已取消")
        raise typer.Abort()

枚举类型

from enum import Enum

class Environment(str, Enum):
    dev = "dev"
    staging = "staging"
    prod = "prod"

@app.command()
def deploy(env: Environment = Environment.staging):
    typer.echo(f"部署到 {env.value}")

Rich 集成

from rich.console import Console
from rich.table import Table
from rich.progress import track

console = Console()

@app.command()
def status():
    table = Table(title="服务状态")
    table.add_column("服务", style="cyan")
    table.add_column("状态", style="green")
    table.add_row("API", "✓ 运行中")
    table.add_row("数据库", "✓ 运行中")
    table.add_row("缓存", "✗ 已停止")
    console.print(table)

@app.command()
def process(files: list[str]):
    for file in track(files, description="处理中..."):
        import time; time.sleep(0.5)
    typer.echo("完成")

命令组嵌套

app = typer.Typer()
user_app = typer.Typer(help="用户管理")
app.add_typer(user_app, name="user")

@user_app.command("list")
def user_list():
    typer.echo("用户列表")

@user_app.command("create")
def user_create(name: str, email: str):
    typer.echo(f"创建用户: {name} ({email})")
python app.py user list
python app.py user create Alice alice@example.com

版本和回调

def version_callback(value: bool):
    if value:
        typer.echo("v1.0.0")
        raise typer.Exit()

@app.callback()
def main(
    version: Annotated[
        Optional[bool],
        typer.Option("--version", "-v", callback=version_callback)
    ] = None,
    verbose: bool = False,
):
    """我的 CLI 工具"""
    if verbose:
        typer.echo("详细模式已开启")

自动补全安装

# 安装 shell 补全
python app.py --install-completion

# 显示补全脚本
python app.py --show-completion

常见问题

Q: 与 Click 的关系?

Typer 基于 Click 构建,但用类型注解替代了装饰器参数。如果需要 Click 的高级功能,可以混合使用。

Q: 如何打包为独立命令?

# pyproject.toml
[project.scripts]
mycli = "my_package.main:app"

Q: 如何处理文件参数?

from pathlib import Path

@app.command()
def process(
    input_file: Path = typer.Argument(..., exists=True, readable=True),
    output_file: Path = typer.Option("output.txt"),
):
    content = input_file.read_text()
    output_file.write_text(content.upper())

参考资源

  • GitHub:https://github.com/tiangolo/typer
  • 文档:https://typer.tiangolo.com/
  • 教程:https://typer.tiangolo.com/tutorial/
  • Click 文档:https://click.palletsprojects.com/