哈喽,大家好!我是风哥,一个资深Python工程师。今天给大家介绍一个超强的命令行工具库——Typer!想写出高大上的命令行程序但是被 argparse 搞得头大?来试试 Typer 吧,它能让你轻松打造出专业级的命令行应用!
安装配置
先把环境搞起来:
1pip install "typer[all]" # 安装完整版,包含所有依赖
基本项目结构建议这样:
1project_root/
2│
3├── cli/
4│ ├── __init__.py
5│ ├── main.py
6│ └── commands/
7│ ├── __init__.py
8│ ├── user.py
9│ └── config.py
10│
11└── setup.py
基础命令创建
来个简单的例子热热身:
1# main.py
2import typer
3from typing import Optional
4from pathlib import Path
5
6app = typer.Typer(help="超强的文件处理工具")
7
8@app.command()
9def process_file(
10 file_path: Path = typer.Argument(..., help="要处理的文件路径"),
11 output: Optional[Path] = typer.Option(None, "--output", "-o", help="输出文件路径"),
12 force: bool = typer.Option(False, "--force", "-f", help="是否强制覆盖")
13):
14 """处理指定文件"""
15 typer.echo(f"处理文件: {file_path}")
16 if not file_path.exists():
17 typer.secho(f"文件不存在!", fg=typer.colors.RED)
18 raise typer.Exit(code=1)
19
20 # 处理逻辑...
21
22if __name__ == "__main__":
23 app()
多命令管理
实际项目中常用的多命令结构:
1# commands/user.py
2import typer
3from typing import List, Optional
4from pydantic import BaseModel
5
6user_app = typer.Typer()
7
8class User(BaseModel):
9 name: str
10 age: int
11 roles: List[str]
12
13@user_app.command()
14def create(
15 name: str = typer.Option(..., prompt=True),
16 age: int = typer.Option(..., prompt=True),
17 roles: List[str] = typer.Option(["user"], "--role", "-r"),
18):
19 """创建新用户"""
20 user = User(name=name, age=age, roles=roles)
21 typer.secho(f"创建用户成功:{user.json()}", fg=typer.colors.GREEN)
22
23@user_app.command()
24def list_users(
25 role: Optional[str] = typer.Option(None, "--role", "-r"),
26 sort_by: str = typer.Option("name", "--sort-by", "-s")
27):
28 """列出所有用户"""
29 # 实现用户列表逻辑...
高级特性
子命令嵌套
1# main.py
2import typer
3from commands import user, config
4
5app = typer.Typer()
6app.add_typer(user.user_app, name="user", help="用户管理相关命令")
7app.add_typer(config.config_app, name="config", help="配置管理相关命令")
8
9@app.callback()
10def callback():
11 """
12 超强的命令行工具
13 """
14 pass
进度条和加载动画
1import time
2from rich.progress import track
3
4@app.command()
5def process_items(count: int = typer.Option(10, help="处理项目数量")):
6 """带进度条的处理"""
7 with typer.progressbar(range(count)) as progress:
8 for value in progress:
9 time.sleep(0.1)
10
11 # 或者用 rich 的进度条
12 for _ in track(range(count), description="处理中..."):
13 time.sleep(0.1)
交互式提示
1@app.command()
2def configure():
3 """交互式配置"""
4 if not typer.confirm("确定要开始配置吗?"):
5 raise typer.Abort()
6
7 username = typer.prompt("请输入用户名")
8 password = typer.prompt("请输入密码", hide_input=True)
9
10 choices = ["开发环境", "测试环境", "生产环境"]
11 env = typer.prompt(
12 "选择环境",
13 type=typer.Choice(choices),
14 show_choices=True
15 )
自定义补全
1def get_completion_items():
2 return ["选项1", "选项2", "选项3"]
3
4@app.command()
5def complete(
6 item: str = typer.Option(
7 ...,
8 autocompletion=get_completion_items
9 )
10):
11 """带自动补全的命令"""
12 typer.echo(f"选择了: {item}")
错误处理和日志
专业的错误处理方案:
1import logging
2from typing import Optional
3from pathlib import Path
4
5# 配置日志
6logging.basicConfig(
7 level=logging.INFO,
8 format="%(asctime)s [%(levelname)s] %(message)s",
9 handlers=[
10 logging.FileHandler("app.log"),
11 logging.StreamHandler()
12 ]
13)
14
15class AppError(Exception):
16 """自定义错误"""
17 def __init__(self, message: str, code: int = 1):
18 self.message = message
19 self.code = code
20
21def handle_error(func):
22 """错误处理装饰器"""
23 def wrapper(*args, **kwargs):
24 try:
25 return func(*args, **kwargs)
26 except AppError as e:
27 typer.secho(f"错误: {e.message}", fg=typer.colors.RED)
28 raise typer.Exit(code=e.code)
29 except Exception as e:
30 logging.exception("未知错误")
31 typer.secho(f"系统错误!", fg=typer.colors.RED)
32 raise typer.Exit(code=1)
33 return wrapper
34
35@app.command()
36@handle_error
37def risky_command():
38 """可能出错的命令"""
39 raise AppError("出错啦!")
测试支持
写测试更方便:
1from typer.testing import CliRunner
2import pytest
3
4runner = CliRunner()
5
6def test_hello():
7 result = runner.invoke(app, ["hello", "--name", "测试"])
8 assert result.exit_code == 0
9 assert "测试" in result.stdout
10
11@pytest.fixture
12def mock_db(mocker):
13 """模拟数据库"""
14 return mocker.patch("your_app.db")
15
16def test_create_user(mock_db):
17 result = runner.invoke(app, [
18 "user", "create",
19 "--name", "张三",
20 "--age", "25"
21 ])
22 assert result.exit_code == 0
23 mock_db.create_user.assert_called_once()
项目打包发布
1# setup.py
2from setuptools import setup, find_packages
3
4setup(
5 name="your-cli-app",
6 version="0.1.0",
7 packages=find_packages(),
8 install_requires=[
9 "typer[all]>=0.4.0",
10 "pydantic>=1.8.0",
11 ],
12 entry_points={
13 "console_scripts": [
14 "your-app=cli.main:app",
15 ],
16 },
17)
温馨提示:记得在 README.md 里写清楚安装和使用方法!
今天的Python学习之旅就到这里啦!记得动手敲代码,有问题随时在评论区问风哥哦。祝大家学习愉快,收获满满!