mcpField
P
python-packaging

python-packaging

github.com/paldom开发348 次安装7 周前更新
代码仓库

关于 python-packaging

帮助将 Python 库构建为可安装发行包并验证产物的技能。它涵盖项目元数据、构建后端选择、基于 uv 的依赖管理,以及 sdist 和 wheel 检查。

主要功能

  1. 01以 pyproject.toml 集中管理打包配置
  2. 02选择 PEP 517 构建后端
  3. 03采用 src/ 布局和 uv
  4. 04验证元数据与构建产物
  5. 05检查 wheel 内容和导入

适用场景

  1. 01为新 Python 库配置打包
  2. 02迁移旧式打包配置
  3. 03验证 wheel 和 sdist 内容
  4. 04排查软件包文件缺失

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/paldom/python-skills --skill python-packaging

安装后在 Claude Code 中输入/python-packaging确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
build-backends.md
scripts/
check_wheel.py

详细介绍

python-packaging

让 Python 库可安装、可构建,并验证构建产物:使用一个 pyproject.toml 作为唯一事实来源,审慎选择 PEP 517 构建后端,采用 src/ 布局,使用 uv 管理依赖并提交锁文件,同时检查 sdist 和 wheel 的实际内容,而不是想当然。本技能可避免以下问题:声明的文件(py.typed、数据文件)悄无声息地从 wheel 中缺失;构建后端无法处理项目的布局或扩展模块;尚未验证依赖解析结果就切换 Poetry 的构建后端;以及调用 pip 导致 uv 管理的环境失去同步。

不适用场景

  • 发布到 PyPI、升级版本、编写变更日志、创建 git 标签、可信发布——如果已安装,请使用 python-release 技能。验证完构建产物后即停止。
  • 选择或配置类型检查器,以及是否要打包 py.typed——使用 python-typing 技能。本技能只验证现有的 py.typed 是否确实进入了构建出的 wheel。
  • [tool.ruff] 配置——使用 python-lint。pytest/coverage 配置——使用 python-testing。GitHub Actions——使用 python-ci。依赖漏洞审计——使用 python-supply-chain。
  • 只需依赖项的单文件脚本——PEP 723 内联脚本元数据加上 uv run script.py 即可,无需打包。

工作流

1. 评估现有情况
ls setup.py setup.cfg MANIFEST.in requirements*.txt poetry.lock Pipfile uv.lock 2>/dev/null
grep -n "build-backend\|tool.poetry\|dependency-groups\|optional-dependencies" pyproject.toml 2>/dev/null
find . -name "*.pyx" -o -name "*.c" -o -name "Cargo.toml" | grep -v .venv | head

更改前记录三项事实:(a)是否有原生扩展,还是纯 Python——这决定构建后端;(b)是扁平布局还是 src/ 布局;(c)仓库现有的开发依赖约定([dependency-groups]、[project.optional-dependencies] 下的 dev extra,或旧式 [tool.uv] dev-dependencies)——沿用该约定,或整体迁移,切勿混用。

2. 选择构建后端
构建后端适用场景避免场景
uv_build纯 Python 库;无需配置即可发现 src/,严格验证元数据含任何 C/Cython/Rust 扩展模块(不支持);需要基于 VCS 标签动态确定版本
hatchling需要通过 hatch-vcs 根据 git 标签动态确定版本;通常被视为通用指南中的默认选择;插件生态丰富希望使用 uv_build 更严格的验证和零配置
setuptools扩展模块(ext_modules)、复杂的旧式配置从零开始的纯 Python 项目——验证较宽松,自动发现可能带来意外

新项目不要采用 poetry-core;整个生态已统一使用 PEP 621 [project] 元数据。注意,uv build 是与构建后端无关的 PEP 517 前端——它驱动 hatchling 或 setuptools 同样得心应手,因此采用 uv 作为项目管理器并不意味着必须迁移构建后端;应将迁移视为独立步骤并加以验证。

[build-system]
requires = ["uv_build>=0.12,<0.13"]
build-backend = "uv_build"

为 uv_build 保留版本上限:它遵循 uv 的版本策略,次版本更新可能包含破坏性更改,因此不设上限的依赖要求可能会在新版本发布当天导致构建失败。Hatchling 和 setuptools 的兼容性规范更保守;通常会设置最低版本(例如 hatchling>=1.27)。 风险说明:uv 整体仍处于 1.0 之前,但 uv_build 构建后端在 PyPI 上明确标记为 Production/Stable,并且自 2025 年年中以来一直是 uv init 的默认后端——不要将两者混为一谈,也不要反向混淆。

完整的权衡说明、[tool.uv.build-backend] 键以及各构建后端的文件包含配置,请参阅:references/build-backends.md。

3. 设置包布局(src/)
my-package/
├── pyproject.toml
├── README.md
├── src/
│   └── my_package/
│       ├── __init__.py
│       └── py.typed        # only if the project already ships one
└── tests/

库应使用 src/:这样,测试和工具导入的就是已安装的包。采用扁平布局时,即使构建出的 wheel 有问题或缺少文件,import my_package 仍会解析到工作目录中的副本,导致打包错误一直被掩盖,直到发布时才暴露。对于新项目,uv init --lib my-package 一步即可生成 src/、py.typed 和 uv_build 构建后端。

uv_build 要求使用 src/<module>/__init__.py,且模块名称必须与规范化后的项目名称相同。如果导入名称不同,请声明:

[tool.uv.build-backend]
module-name = "actual_import_name"
4. 编写 [project] 元数据

可发布的最小配置集——validate-pyproject(步骤 6)会据此检查:

[project]
name = "my-package"
version = "0.1.0"
description = "One line, benefit first."
readme = "README.md"
license = "MIT"                      # SPDX expression (PEP 639; needs a current backend)
requires-python = ">=3.10"
authors = [{ name = "Your Name", email = "[email protected]" }]
dependencies = ["httpx>=0.27"]
classifiers = ["Programming Language :: Python :: 3"]

[project.scripts]
my-cli = "my_package.cli:main"       # callable must take no required args; parse sys.argv inside

[dependency-groups]
dev = ["pytest>=8"]

开发工具应放在 PEP 735 [dependency-groups] 中(由 uv add --dev 写入),而不是放在已发布的 extra 中:消费者可以从 PyPI 安装 [project.optional-dependencies] 下的 extras,但不能安装依赖组。dev extra 会把你的工具链泄漏给下游解析器。extras 用于可选的运行时功能(例如 my-package[cli])。

5. 使用 uv 管理项目
uv init --lib my-package            # new projects only (uv >= 0.12 also declares a build system for plain `uv init`; `--no-package` opts out)
uv add httpx                        # runtime dep -> [project.dependencies] + uv.lock
uv add --dev pytest                 # dev dep -> [dependency-groups].dev
uv sync                             # build .venv from uv.lock (--no-dev in production)
uv lock --upgrade-package httpx     # targeted upgrade; rest of the lockfile intact
uv run pytest                       # run tools in the env, no activation

提交 uv.lock;不要手动编辑它。每次运行 uv run 前,uv 都会检查锁文件是否与 pyproject.toml 保持同步,以及环境是否与锁文件保持同步,这正是结果可复现的原因。不要在 uv 项目中调用 pip——它会在锁文件不知情的情况下修改 .venv。

迁移:

  • 从 setup.py/setup.cfg 迁移——先逐一梳理所有配置段:[options] 中的元数据移到 [project],package_data/MANIFEST.in 指令转换为后端的文件包含配置,工具配置段([tool:pytest]、[flake8]、[mypy])移到 pyproject.toml 或对应工具的配置文件中。先用旧配置构建一次,再用新配置构建一次,对比 wheel/sdist 文件列表,然后再删除 setup.py、setup.cfg 和 MANIFEST.in(步骤 6–8 会验证结果)。例外:带有 C 扩展的 setuptools 项目仍需要通过 setup.py 配置 ext_modules——只保留这部分。
  • 从 Poetry 迁移——运行 uvx 'migrate-to-uv==0.12.0'(固定版本:不固定版本的 uvx 会执行最近发布到 PyPI 的版本,这既无法复现,也会造成供应链安全漏洞;应有意升级固定版本)。该工具会将 [tool.poetry.*] 映射到 PEP 621,并将 poetry.lock 映射为 uv.lock。最后再将 [build-system] 从 poetry-core 切换出去,确保 uv lock && uv sync 能顺利解析——先切换后端,是同时调试两项迁移的典型方式。
  • 从 requirements.txt 迁移——运行 uv add -r requirements.txt(如果有 constraints.txt,再加上 -c constraints.txt),然后删除该文件。如果下游工具仍需要它,使用 uv export 生成。
6. 验证元数据
uvx --from 'validate-pyproject[all]==0.26' validate-pyproject pyproject.toml

即使 pyproject.toml 能解析为有效 TOML,也仍可能违反打包 PEP 模式(许可证格式错误、键位置不当、入口点格式错误)——这些问题是格式化工具和 TOML 解析器无法发现的。每次修改元数据后都运行此检查;它开销很小。

7. 构建
uv build        # sdist (.tar.gz) + wheel (.whl) into dist/

在任何前端下调试 uv_build 时,设置 RUST_LOG=uv=debug。无 uv 后备方案(仅在 uv 不可用的环境中使用一次):python -m pip install build==1.6.0 && python -m build 会生成相同的构件,而 pip install -e . --group dev(pip >= 25.1;依赖组不是 extras)可替代开发安装中的 uv sync,但会牺牲锁文件的可复现性。

8. 验证构件——绝不跳过

运行随附的检查器(相对于此技能文件夹的路径):

python3 "${CLAUDE_SKILL_DIR}/scripts/check_wheel.py" --project-root /path/to/project /path/to/project/dist

该检查器只读;遇到以下情况时会以非零状态码退出并输出 ERROR 行:wheel 缺少 METADATA/WHEEL/RECORD;源代码树中存在的 py.typed 未包含在 wheel 中;tests/、docs/ 或类似目录泄漏到 wheel 中;或 sdist 缺少 pyproject.toml。手动检查的等效命令:unzip -l dist/*.whl 和 tar -tzf dist/*.tar.gz。

然后对 wheel 本身(而不是源代码树)进行冒烟测试:

uv run --isolated --no-project --with dist/my_package-0.1.0-py3-none-any.whl \
  python -c "import my_package; print(my_package.__name__)"

输出规范——完成后的预期状态

  • pyproject.toml 是唯一的打包配置;setup.py/setup.cfg/MANIFEST.in/requirements.txt 均已删除(若 setup.py 仅用于 ext_modules,则可保留)。
  • validate-pyproject 以 0 状态码退出。
  • uv build 同时生成 dist/*.tar.gz 和 dist/*.whl。
  • scripts/check_wheel.py 以 0 状态码退出——已验证 wheel 中包含预期文件(包括源代码附带 py.typed 时的该文件);没有测试或文档泄漏。
  • 构建出的 wheel 可在隔离环境中导入。
  • uv.lock 存在且已提交。

失败模式与注意事项

  • 仓库中有 py.typed,但 wheel 中缺失。 不能假设任何后端都会打包任意非 .py 文件:setuptools 需要 [tool.setuptools.package-data],hatchling 仅在 wheel 目标指向包目录时才会打包其中的文件, uv_build 会纳入模块根目录下的文件。使用缺少该标记的 wheel 的用户会悄悄地将整个 API 都视为 Any——请检查构建产物,而非源代码树。各后端的代码片段见 references/build-backends.md。
  • uv_build 报“找不到模块”或元数据验证失败。 默认情况下,它会在 src/<module>/ 或 ./<module>/ 中查找一个以规范化项目名称命名的模块。如需声明不同的名称、多个模块或命名空间包,请使用 [tool.uv.build-backend] 中的 module-name = [...] / module-root / namespace(建议使用显式列表,而非会禁用安全检查的 namespace = true)。
  • uv 发布新版本后构建失败。 requires = ["uv_build"] 未限定版本范围——请恢复 >=X,<X+1 限定(步骤 2)。
  • Poetry 迁移未完成。 [build-system] 已切换,但实际依赖列表仍留在 [tool.poetry.dependencies] 中——构建产物会包含错误或缺失的元数据。先处理元数据,再验证依赖解析,最后切换后端。
  • Heisenbug 环境。 有人在 uv 项目中运行了 pip install;下一次 uv run 会悄悄还原更改。两个方向看起来都像是不稳定。只有 uv add/uv sync 会修改环境。
  • 测试通过,但已安装的包无法使用。 扁平布局导入了工作目录中的副本,掩盖了 wheel 中缺失的文件。src/ 布局加上步骤 8 的 wheel 冒烟测试可以发现此问题。
  • tests/ 被打包进 wheel。 setuptools 的自动发现会纳入任何可导入的顶层目录。请限制发现范围([tool.setuptools.packages.find] where = ["src"]),或改用 src/ 布局;check_wheel.py 会标记此类泄漏。
  • 开发用额外依赖被发布到 PyPI。 将开发工具声明为 [project.optional-dependencies] dev,会让每位用户都能安装此额外依赖组。请使用 [dependency-groups] 配置工具;额外依赖组仅用于可选的运行时功能。
  • TOML 有效,但元数据无效。 格式化工具和 TOML 解析器接受的文件仍可能违反打包架构的要求——这正是步骤 6 的用途。
  • 性能传言。 关于 uv_build 可大幅提升构建速度的说法,依据的是从业者的博客文章,而非受控基准测试。请仅将其视为趋势,不要将具体数字当作事实传播。

随附文件

  • references/build-backends.md — 深入介绍后端的权衡、[tool.uv.build-backend] 参考、各后端的文件纳入规则(py.typed、数据文件)、迁移路径,以及从旧工具到 uv 的命令对照表。
  • scripts/check_wheel.py — 只读的 sdist/wheel 验证工具;每次构建后运行;失败时以非零状态退出,并输出机器可读的 ERROR 行。

评论

支持 Markdown