前言¶
Python 项目里常见的一套工具链是这样的:pip 装依赖,virtualenv 管环境,flake8 做检查,black 做格式化,isort 排 import,mypy 做类型检查,pre-commit 再把这些钩到 Git 上。配置往往散落在 requirements.txt、setup.py、.flake8、mypy.ini 里,Agent 写代码时还经常随手执行 pip install,或者手动 source .venv/bin/activate。
Astral 这一侧已经把包管理、检查、格式化收成了 uv、ruff,类型检查也有同门的 ty。问题是:人可以按文档慢慢迁,Agent 默认仍可能沿用旧命令。Trail of Bits 把内部模板 cookiecutter-python 里的选择,写成了 Agent Skill modern-python,用来约束新项目怎么建、旧项目怎么迁、单文件脚本怎么声明依赖。
这是什么¶
modern-python 是 Trail of Bits 在 trailofbits/skills 市场里发布的一条 Skill,插件作者署名为 William Tan。官方描述是:用 uv、ruff、ty 配置 Python 项目;在创建项目、写带依赖的独立脚本,或从 pip / Poetry / mypy / black 迁移时使用。
它基于通用的 SKILL.md 格式,因此在 Claude Code、Codex CLI、Cursor 这类支持 Agent Skill 的工具里都可以加载。仓库把它归在 Development 一类,和安全审计类 Skill 放在同一个 marketplace 里。实践规范来自 Trail of Bits 自己的 cookiecutter 模板,不是另一套平行标准。
Skill 明确写了不适用的情况:用户要求保留旧工具链时不要强行替换;需要 Python 3.11 以下时不要用这套工具;Python 不是主语言的混合仓库也不要套上去。
核心工具¶
Skill 把推荐工具和被替换的旧工具列成了一张表,交叉对照 GitHub 上的 SKILL.md 与官方文档后,内容一致:
| 工具 | 作用 | 替换对象 |
|---|---|---|
| uv | 包与依赖管理 | pip、virtualenv、pip-tools、pipx、pyenv |
| ruff | 检查和格式化 | flake8、black、isort、pyupgrade、pydocstyle |
| ty | 类型检查 | mypy、pyright |
| pytest | 测试与覆盖率 | unittest |
| prek | Git hooks | pre-commit |
ty 来自 Astral(与 uv、ruff 同一团队)。Astral 官方文档把它定位为用 Rust 写的类型检查器,当前仍处于 beta,Skill 仍推荐用它替代 mypy / pyright。实际落地时要把这点算进风险:规则和诊断在小版本之间仍可能变。
prek 是 j178/prek,Rust 实现、单二进制、兼容已有的 .pre-commit-config.yaml。Skill 把它写成比 pre-commit 更快、且不依赖 Python 运行时的替代。
除开发工具外,Skill 还附带一组安全相关能力,主要跑在 pre-commit 或 CI 里:
| 工具 | 作用 | 运行时机 |
|---|---|---|
| shellcheck | Shell 脚本检查 | pre-commit |
| detect-secrets | 密钥检测 | pre-commit |
| actionlint | GitHub Actions 语法校验 | pre-commit、CI |
| zizmor | Workflow 安全审计 | pre-commit、CI |
| pip-audit | 依赖漏洞扫描 | CI、手工 |
| Dependabot | 依赖自动更新 | 定时 |
作为 Claude Code 插件安装时,仓库还带了一个 SessionStart hook:给 python、pip、pipx、uv 加上 PATH shim。Agent 直接跑 python 或 pip install 会被拦截,并提示改成 uv run / uv add 这类命令。grep python、which python 不受影响,因为这时 python 只是参数,不是被执行的命令。只把 SKILL.md 拷进 Skill 目录时,不一定带上这个 hook,以实际安装方式为准。
几条硬性约定¶
Skill 把常见反模式写成了对照表,写作时代理应优先按「右侧」执行:
| 不要这样做 | 改用 |
|---|---|
[tool.ty] 里写 python-version |
[tool.ty.environment] 下的 python-version |
uv pip install |
uv add 和 uv sync |
手工改 pyproject.toml 加依赖 |
uv add / uv remove |
hatchling 作为 build backend |
uv_build(多数项目够用) |
| Poetry | uv |
requirements.txt |
脚本用 PEP 723,项目用 pyproject.toml |
| mypy / pyright | ty |
用 [project.optional-dependencies] 装开发工具 |
[dependency-groups](PEP 735) |
source .venv/bin/activate |
uv run |
| pre-commit | prek |
三条原则在文档里反复出现:依赖只通过 uv add / uv remove 管理;命令一律 uv run,不要手工激活虚拟环境;开发/测试/文档依赖放进 [dependency-groups],不要放进给用户安装的 extras。
安装与启用¶
Claude Code¶
官方 marketplace 安装分两步。先加入 Trail of Bits 的插件市场:
/plugin marketplace add trailofbits/skills
再安装 modern-python 插件:
/plugin install trailofbits/skills/plugins/modern-python
也可以先执行 /plugin menu 浏览后再装。官方 Quick Start 里给出的调用示例是:
Use the modern-python skill to create a new Python project with uv, ruff, and pytest
Codex CLI¶
仓库 README 写明 Codex 可以直接加载 Claude 的 marketplace,不需要额外的 sidecar 元数据:
codex plugin marketplace add trailofbits/skills
codex plugin list
codex plugin add modern-python@trailofbits
最后一条里的插件名与仓库中 plugins/modern-python 目录名一致。
通用 Skill 安装(Cursor 等)¶
skills.sh 上的安装命令是:
npx skills add https://github.com/trailofbits/skills --skill modern-python
这条命令按 Agent Skills 的通用目录约定把 SKILL.md 装进当前工具使用的 skills 路径。装好后,直接描述任务即可,例如「按 modern-python 给这个仓库配 uv 和 ruff」,或「把现有 pip + requirements.txt 迁到 uv」。
典型用法¶
Skill 用一棵决策树区分四类工作:单文件脚本走 PEP 723;不打算分发的多文件项目走最小 uv 配置;可复用的包走完整项目配置;已有仓库走迁移指南。
1. 最小项目¶
不打算发布到 PyPI 的多文件项目,官方 Quick Start 如下:
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 .
依赖进 pyproject.toml 和 uv.lock,一次性试用某个包则用 uv run --with,不要写进项目依赖:
uv run --with requests python -c "import requests; print(requests.get('https://httpbin.org/ip').json())"
uv run --with httpx pytest
2. 完整包项目¶
从零开始时,Skill 会先问是否用 Trail of Bits 的 cookiecutter 一次生成完整脚手架(含 pyproject.toml、src/ 布局、依赖组、hooks、GitHub Actions 和安全扫描):
uvx cookiecutter gh:trailofbits/cookiecutter-python
不用模板时,用 uv 创建可分发的包:
uv init --package myproject
cd myproject
生成的目录结构是:
myproject/
├── pyproject.toml
├── README.md
├── src/
│ └── myproject/
│ └── __init__.py
└── .python-version
SKILL.md 里给出的关键配置如下。依赖组不要手工改,用 uv add --group 维护:
[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"
安装依赖:
uv sync --all-groups
# 或只装某一组
uv sync --group dev
Skill 还建议在仓库里放一个 Makefile,把日常命令收口到 uv run:
.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
3. 单文件脚本:PEP 723¶
只有一个文件、但又需要第三方库时,不要再配一份 requirements.txt。Skill 的参考文档要求把元数据写进脚本注释,用 uv run 执行:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests",
# "rich",
# ]
# ///
import requests
from rich import print
response = requests.get("https://httpbin.org/ip")
print(response.json())
uv run script.py
也可以用 uv 维护脚本依赖:
uv init --script myscript.py
uv add --script myscript.py requests
uv remove --script myscript.py requests
官方参考也写了限制:PEP 723 没有 dependency groups、不能 editable install、没有 lockfile,多次运行时解析到的版本可能不同。需要这些能力时改用完整的 pyproject.toml。
4. 从旧工具链迁移¶
用户明确要求迁移时,Skill 才走这条路径;用户要留 pip / Poetry / mypy 时,文档要求尊重现有流程。
从 requirements.txt + pip 迁到 uv:
uv init --bare
# 逐个添加,不要直接改 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 "Failed to add: $pkg"
done
uv sync
随后删除 requirements.txt、requirements-dev.txt 和旧虚拟环境(venv/、.venv/),并把 uv.lock 纳入版本控制。
从 setup.py / setup.cfg 迁移时:先 uv init --bare,把 install_requires 用 uv add 搬进去,开发依赖用 uv add --group dev,再把名称、版本、描述等元数据拷到 [project],最后删除 setup.py、setup.cfg、MANIFEST.in。
从 flake8 + black + isort 迁到 ruff:
uv remove flake8 black isort
# 删除 .flake8,以及 pyproject.toml 里的 [tool.black]、[tool.isort]
uv add --group dev ruff
uv run ruff format .
uv run ruff check --fix .
从 mypy / pyright 迁到 ty:
uv remove mypy pyright
# 删除 mypy.ini、pyrightconfig.json,以及 [tool.mypy] / [tool.pyright]
uv add --group dev ty
uv run ty check src/
描述里提到可以从 Poetry 迁到 uv,但 SKILL.md 的迁移章节没有单独列出 Poetry 的逐步命令,只在反模式表里把 Poetry 标成应替换为 uv。遇到 Poetry 项目时,应按仓库里的 references/migration-checklist.md 核对,不要把 pip 那套步骤直接套上去。
适用场景与注意事项¶
适合用这条 Skill 的情况包括:新建 Python 包或内部项目;给已有仓库补 pyproject.toml、lint、测试;写带第三方依赖的单文件脚本;用户明确要求从 pip / flake8 / black / mypy 迁走。
不适合的情况文档写得很直接:必须支持 Python 3.11 以下;用户明确要留 Poetry、mypy、black;Python 只是仓库里的配角。
使用时还有几处需要留意:
- 最低版本是 3.11。
requires-python = ">=3.11"和 ruff 的target-version = "py311"是配套的,不能只改其中一个。 - ty 仍是 beta。 Skill 把它当作默认类型检查器,但 Astral 自己说明 API 与诊断都还不稳定。对类型检查结果很敏感的仓库,迁移前要先在分支上跑一遍
uv run ty check src/,看误报是否可接受。 - 覆盖率门槛默认 80%。 旧项目第一次接 pytest-cov 时,这个数字很容易把 CI 打红,需要按仓库现状调整,而不是原样粘贴。
ruff的select = ["ALL"]很严。 官方配置用ignore关掉了D(pydocstyle)、COM812、ISC001,其余规则都开。接到老代码上会有大量告警,需要按模块逐步收紧,而不是指望一次--fix全部干净。- 插件 hook 只在插件安装路径下生效。 Claude Code / Codex 按 marketplace 安装时会拦截裸
python/pip;仅同步SKILL.md时,Agent 仍可能写出旧命令,提示词里最好写明「不要用 pip,用 uv add / uv run」。 - 参考文档比 SKILL.md 更细。 同目录下还有
pyproject.md、uv-commands.md、ruff-config.md、testing.md、pep723-scripts.md、prek.md、security-setup.md、dependabot.md、migration-checklist.md。做完整迁移或安全扫描时,应让 Agent 继续读这些文件,而不是只靠 SKILL 正文里的摘要。
小结¶
modern-python 做的事情很具体:把 Trail of Bits cookiecutter 模板里已经在用的工具链(uv、ruff、ty、pytest、prek)写成 Agent 可执行的规范,并附带从 pip / setup.py / flake8 / mypy 迁过来的步骤。它解决的不是「Python 该不该现代化」,而是 Agent 默认仍会 pip install、手工改 pyproject.toml、激活虚拟环境这一类操作。
官方地址:
- Skill 目录:https://github.com/trailofbits/skills/tree/main/plugins/modern-python/skills/modern-python
- 插件说明:https://github.com/trailofbits/skills/tree/main/plugins/modern-python
- 文档站:https://trailofbits-skills.mintlify.app/plugins/modern-python
- skills.sh:https://skills.sh/trailofbits/skills/modern-python
- 模板仓库:https://github.com/trailofbits/cookiecutter-python