关于 packaging-python-libraries
面向 Python 库开发者的打包与发布指南,覆盖 `pyproject.toml`、构建、PyPI 发布和依赖配置。特别强调检查 wheel 内容,并从干净环境验证安装产物。
主要功能
- 01配置 `pyproject.toml`
- 02使用 `uv` 构建发行包
- 03检查 PyPI 发布元数据
- 04配置依赖与包数据
- 05检查 wheel 文件内容
- 06从干净环境验证安装
适用场景
- 01为 Python 库准备发布配置
- 02构建并检查 sdist 和 wheel
- 03排查发行包遗漏子包或数据文件
- 04发布前验证安装包中的真实模块
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wdm0006/ python-skills --skill packaging-python-libraries安装后在 Claude Code 中输入/packaging-python-libraries确认已加载。
附带文件CONDA.md
详细介绍
Python 库打包
pyproject.toml 基础知识
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
version = "1.0.0"
description = "Short description"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
dependencies = []
[project.optional-dependencies]
dev = ["pytest>=7.0", "ruff>=0.1", "mypy>=1.0"]
[project.urls]
Homepage = "https://github.com/user/package"
Documentation = "https://package.readthedocs.io"
[project.scripts]
mycli = "my_package.cli:main"
[tool.setuptools.packages.find]
where = ["src"]
构建
uv build # Creates sdist + wheel in dist/
uvx twine check dist/* # Validate metadata
发布到 PyPI
优先通过 CI 使用可信发布(无需存储令牌)——请参阅下文的 CI 部分。手动发布时,请通过 uvx 使用 twine 上传:
uvx twine upload --repository testpypi dist/* # Test first
uvx twine upload dist/* # Production (uses a PyPI token)
GitHub Actions(可信发布)
发布由 managing-python-releases 技能中的规范标签触发发布工作流自动完成——一个流水线使用 uv 构建,并通过可信发布进行发布。请参阅 ../release-management/AUTOMATION.md;不要在此处维护第二份副本。
依赖项最佳实践
# DO: Minimum versions
dependencies = ["requests>=2.28", "click>=8.0"]
# DON'T: Exact pins (locks users)
dependencies = ["requests==2.28.1"]
# DO: Optional for features
[project.optional-dependencies]
cli = ["click>=8.0"]
包含包数据
[tool.setuptools.package-data]
my_package = ["py.typed", "data/*.json"]
from importlib.resources import files
data = files("my_package.data").joinpath("file.json").read_text()
直接引用依赖项需要实际构建
像 toolkit @ https://example.com/toolkit.whl 这样的依赖项,在安装试运行时可能无需调用构建后端就能解析。使用 Hatchling 时,实际构建 wheel 会失败,除非明确允许直接引用:
[project]
dependencies = [
"toolkit @ https://example.com/releases/toolkit.whl",
]
[tool.hatch.metadata]
allow-direct-references = true
在这种情况下,不要使用 uv pip install --dry-run . 作为打包检查。它可能在没有构建项目的情况下报告成功。请运行 uv build(或实际执行 uv pip install .),让已配置的后端验证元数据:
uv build
uvx twine check dist/*
如需详细模板,请参阅:
- ../project-setup/PYPROJECT.md - 完整注释版 pyproject.toml(规范版本)
- CONDA.md - Conda / conda-forge 打包指南
验证构建产物(构建成功不代表 wheel 正确)
uv build 成功只说明后端运行了,并不代表 wheel 包含了你的代码。构建后端通过配置选择文件——hatchling 的 [tool.hatch.build.targets.wheel](only-include / packages / include),setuptools 的 [tool.setuptools.packages.find]。配置错误时,后端会毫无提示地打包出缺少子包或数据文件的 wheel。twine check 也无法发现问题——它验证的是元数据,而不是内容。
发布前务必检查 wheel,并在干净环境中安装:
uv build
uv run python -m zipfile -l dist/*.whl # list every file the wheel contains
# ^ confirm ALL your subpackages (my_pkg/, my_pkg/sub/) and data files are there,
# not just the top-level module.
最常见的陷阱是文件选择范围过窄。以下配置只会打包 server.py,并悄悄丢弃整个 server/ 包和 data/:
# DON'T — over-narrow include drops everything else
[tool.hatch.build]
only-include = ["server.py"]
# DO — include the package (and any data dirs); let the backend walk it
[tool.hatch.build.targets.wheel]
packages = ["src/my_pkg"]
名称冲突陷阱: 绝不要同时打包顶层模块 foo.py 和包目录 foo/。包会遮蔽模块,因此 import foo 会解析到(通常几乎为空的)foo/__init__.py;而控制台入口 foo = "foo:main" 会因该包没有 main 而失败。二选一——通常保留包——并删除另一个。
然后在干净安装环境中验证,而不是在源码树中验证:
uv venv /tmp/verify && uv pip install --python /tmp/verify/bin/python dist/*.whl
cd /tmp && /tmp/verify/bin/python -c "import my_pkg; my_pkg.submodule.real_func"
mycli --help # exercise each console script too
请从仓库根目录以外的目录运行——否则 import my_pkg 会从 sys.path 中的源码树加载,即使 wheel 为空也会“正常运行”。并断言一个真实符号(my_pkg.submodule.real_func),绝不要只检查顶层名称能否导入:import foo 可能从一个遮蔽模块的空包中成功导入,无法证明任何问题。只执行 import foo; print("ok") 的 CI 冒烟测试会误报成功——无论分发的包是否可用,它都会通过。
检查清单
Before Release:
- [ ] pyproject.toml valid
- [ ] README.md informative
- [ ] LICENSE file exists
- [ ] Version set correctly
- [ ] twine check passes
- [ ] `uv run python -m zipfile -l dist/*.whl` shows every subpackage + data file
- [ ] Direct-reference dependencies pass a real backend build (not only install dry-run)
- [ ] No module/package name collision (no foo.py AND foo/)
- [ ] Installed the wheel into a clean venv and imported a real submodule
symbol from a directory outside the repo (not just the top-level name)
- [ ] Each console script runs after a clean install
After Release:
- [ ] pip install works
- [ ] Import works
- [ ] GitHub release created
了解更多
此技能基于 Will McGinnis 撰写的 《高质量 Python 库开发指南》中的 Distribution(分发)部分。如需深入了解,请参阅以下文章: