name: modern-python description: 使用现代工具(uv、ruff、ty)配置Python项目。适用于创建项目、编写独立脚本或从pip/Poetry/mypy/black迁移。
现代Python
基于trailofbits/cookiecutter-python的现代Python工具和最佳实践指南。
何时使用此技能
- 创建新的Python项目或包
- 设置
pyproject.toml配置 - 配置开发工具(代码检查、格式化、测试)
- 使用外部依赖编写Python脚本
- 从传统工具迁移(当用户请求时)
何时不使用此技能
- 用户希望保留传统工具:如果明确请求,尊重现有工作流程
- 需要Python < 3.11:这些工具针对现代Python版本
- 非Python项目:混合代码库中Python不是主要语言的情况
需要避免的反模式
| 避免 | 使用替代 |
|---|---|
[tool.ty] python-version |
[tool.ty.environment] python-version |
uv pip install |
uv add 和 uv sync |
| 手动编辑pyproject.toml添加依赖 | uv add <pkg> / uv remove <pkg> |
hatchling 构建后端 |
uv_build(更简单,适用于大多数情况) |
| Poetry | uv(更快、更简单、更好的生态系统集成) |
| requirements.txt | 脚本使用PEP 723,项目使用pyproject.toml |
| mypy / pyright | ty(更快,来自Astral团队) |
[project.optional-dependencies] 用于开发工具 |
[dependency-groups](PEP 735) |
手动虚拟环境激活(source .venv/bin/activate) |
uv run <cmd> |
| pre-commit | prek(更快,无需Python运行时) |
关键原则:
- 始终使用
uv add和uv remove管理依赖 - 永远不要手动激活或管理虚拟环境——对所有命令使用
uv run - 使用
[dependency-groups]用于开发/测试/文档依赖,而不是[project.optional-dependencies]
决策树
你在做什么?
│
├─ 具有依赖的单文件脚本?
│ └─ 使用PEP 723内联元数据(./references/pep723-scripts.md)
│
├─ 新的多文件项目(不发布)?
│ └─ 最小uv设置(见下面的快速开始)
│
├─ 新的可重用包/库?
│ └─ 完整项目设置(见下面的完整设置)
│
└─ 迁移现有项目?
└─ 见下面的迁移指南
工具概述
| 工具 | 目的 | 替代 |
|---|---|---|
| uv | 包/依赖管理 | pip, virtualenv, pip-tools, pipx, pyenv |
| ruff | 代码检查和格式化 | flake8, black, isort, pyupgrade, pydocstyle |
| ty | 类型检查 | mypy, pyright(更快替代) |
| pytest | 测试覆盖 | unittest |
| prek | 预提交钩子(设置) | pre-commit(更快,Rust原生) |
安全工具
| 工具 | 目的 | 何时运行 |
|---|---|---|
| shellcheck | Shell脚本检查 | 预提交 |
| detect-secrets | 秘密检测 | 预提交 |
| actionlint | 工作流程语法验证 | 预提交, CI |
| zizmor | 工作流程安全审计 | 预提交, CI |
| pip-audit | 依赖漏洞扫描 | CI, 手动 |
| Dependabot | 自动依赖更新 | 计划任务 |
见security-setup.md获取配置和使用。
快速开始:最小项目
对于不打算发布的简单多文件项目:
# 使用uv创建项目
uv init myproject
cd myproject
# 添加依赖
uv add requests rich
# 添加开发依赖
uv add --group dev pytest ruff ty
# 运行代码
uv run python src/myproject/main.py
# 运行工具
uv run pytest
uv run ruff check .
完整项目设置
如果从零开始,询问用户是否更愿意使用Trail of Bits的cookiecutter模板来引导一个已预配置工具的完整项目。
uvx cookiecutter gh:trailofbits/cookiecutter-python
1. 创建项目结构
uv init --package myproject
cd myproject
这会创建:
myproject/
├── pyproject.toml
├── README.md
├── src/
│ └── myproject/
│ └── __init__.py
└── .python-version
2. 配置pyproject.toml
见pyproject.md获取完整配置参考。
关键部分:
[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = []
[dependency-groups]
dev = [{include-group = "lint"}, {include-group = "test"}, {include-group = "audit"}]
lint = ["ruff", "ty"]
test = ["pytest", "pytest-cov"]
audit = ["pip-audit"]
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["ALL"]
ignore = ["D", "COM812", "ISC001"]
[tool.pytest]
addopts = ["--cov=myproject", "--cov-fail-under=80"]
[tool.ty.terminal]
error-on-warning = true
[tool.ty.environment]
python-version = "3.11"
[tool.ty.rules]
# 新项目从第一天起就严格
possibly-unresolved-reference = "error"
unused-ignore-comment = "warn"
3. 安装依赖
# 安装所有依赖组
uv sync --all-groups
# 或安装特定组
uv sync --group dev
4. 添加Makefile
.PHONY: dev lint format test build
dev:
uv sync --all-groups
lint:
uv run ruff format --check && uv run ruff check && uv run ty check src/
format:
uv run ruff format .
test:
uv run pytest
build:
uv build
迁移指南
当用户请求从传统工具迁移时:
从requirements.txt + pip
首先,确定代码性质:
对于独立脚本:转换为PEP 723内联元数据(见pep723-scripts.md)
对于项目:
# 在现有项目中初始化uv
uv init --bare
# 使用uv添加依赖(不通过编辑pyproject.toml)
uv add requests rich # 添加每个包
# 或从requirements.txt导入(在添加前审查每个包)
# 注意:复杂版本说明符可能需要手动处理
grep -v '^#' requirements.txt | grep -v '^-' | grep -v '^\s*$' | while read -r pkg; do
uv add "$pkg" || echo "添加失败: $pkg"
done
uv sync
然后:
- 删除
requirements.txt、requirements-dev.txt - 删除虚拟环境(
venv/、.venv/) - 将
uv.lock添加到版本控制
从setup.py / setup.cfg
- 运行
uv init --bare创建pyproject.toml - 使用
uv add添加来自install_requires的每个依赖 - 使用
uv add --group dev添加开发依赖 - 将非依赖元数据(名称、版本、描述等)复制到
[project] - 删除
setup.py、setup.cfg、MANIFEST.in
从flake8 + black + isort
- 通过
uv remove移除flake8、black、isort - 删除
.flake8、pyproject.toml [tool.black]、[tool.isort]配置 - 添加ruff:
uv add --group dev ruff - 添加ruff配置(见ruff-config.md)
- 运行
uv run ruff check --fix .应用修复 - 运行
uv run ruff format .格式化
从mypy / pyright
- 通过
uv remove移除mypy/pyright - 删除
mypy.ini、pyrightconfig.json或[tool.mypy]/[tool.pyright]部分 - 添加ty:
uv add --group dev ty - 运行
uv run ty check src/
快速参考:uv命令
| 命令 | 描述 |
|---|---|
uv init |
创建新项目 |
uv init --package |
创建可发布包 |
uv add <pkg> |
添加依赖 |
uv add --group dev <pkg> |
添加到依赖组 |
uv remove <pkg> |
移除依赖 |
uv sync |
安装依赖 |
uv sync --all-groups |
安装所有依赖组 |
uv run <cmd> |
在venv中运行命令 |
uv run --with <pkg> <cmd> |
使用临时依赖运行 |
uv build |
构建包 |
uv publish |
发布到PyPI |
使用--with的临时依赖
对于需要项目中不存在的包的一次性命令,使用uv run --with:
# 使用临时包运行Python
uv run --with requests python -c "import requests; print(requests.get('https://httpbin.org/ip').json())"
# 使用临时依赖运行模块
uv run --with rich python -m rich.progress
# 多个包
uv run --with requests --with rich python script.py
# 结合项目依赖(添加到现有venv)
uv run --with httpx pytest # 项目依赖 + httpx
何时使用--with vs uv add:
uv add:包是项目依赖(放入pyproject.toml/uv.lock)--with:一次性使用、测试或项目上下文外的脚本
见uv-commands.md获取完整参考。
快速参考:依赖组
[dependency-groups]
dev = ["ruff", "ty"]
test = ["pytest", "pytest-cov", "hypothesis"]
docs = ["sphinx", "myst-parser"]
安装:uv sync --group dev --group test
最佳实践检查清单
- [ ] 对包使用
src/布局 - [ ] 设置
requires-python = ">=3.11" - [ ] 配置ruff,
select = ["ALL"]并明确忽略 - [ ] 使用ty进行类型检查
- [ ] 强制执行测试覆盖最小(80%+)
- [ ] 使用依赖组而不是extras用于开发工具
- [ ] 将
uv.lock添加到版本控制 - [ ] 对独立脚本使用PEP 723
下一步阅读
- migration-checklist.md - 逐步迁移清理
- pyproject.md - 完整pyproject.toml参考
- uv-commands.md - uv命令参考
- ruff-config.md - Ruff检查/格式化配置
- testing.md - pytest和覆盖设置
- pep723-scripts.md - PEP 723内联脚本元数据
- prek.md - 使用prek的快速预提交钩子
- security-setup.md - 安全钩子和依赖扫描
- dependabot.md - 自动依赖更新