进阶篇 · Advanced
lv.3 进阶
kp-036
打包与发布
1. 一句话定义
现代 Python 项目以 pyproject.toml 为唯一项目定义(元数据、依赖、构建后端、工具配置),python -m build 产出 sdist/wheel,上传 PyPI 或私有索引即完成发布。
2. 为什么重要
"能 pip install 的项目"才谈得上复用与协作。pyproject.toml(PEP 621/517/518)统一了曾经碎片化的 setup.py/setup.cfg/requirements 时代,是读懂任何现代开源仓库的第一站。
3. 前置知识
4. 核心概念
toml
# pyproject.toml(最小骨架)
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "mylib"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["httpx>=0.27"]
[project.optional-dependencies] # extras:pip install mylib[cli]
cli = ["typer"]
[tool.pytest.ini_options] # 工具配置也能住在这里
testpaths = ["tests"]- sdist(.tar.gz):源码分发,需在用户侧构建。
- wheel(.whl):预构建分发,
pip install直接解包——现代默认,尽量只发 wheel。 - 构建后端:hatchling/setuptools/flit/uv,只管"怎么从源码造出 wheel"。
5. 原理与机制
bash
python -m build # 产出 dist/*.tar.gz + *.whl
uv build # uv 版,更快
twine check dist/* # 元数据体检
twine upload dist/* # 上传 PyPI(或 --repository-url 私有源)
uv publish # uv 全家桶版
# 应用类项目:不发布,安装成命令行工具
uv tool install . # 或 pipx install .(kp-043)src 布局 + 可编辑安装是本地开发的黄金组合:
bash
uv pip install -e . # editable:源码即改即生效6. 关键事实(模型/图示)
text
项目演进三层(不必一步到位):
① 单脚本 → 直接 python x.py
② 库/多模块项目 → pyproject.toml + src 布局 + editable 安装
③ 面向社区 → 版本管理 + changelog + PyPI 发布 + CI
版本语义:major.minor.patch(破坏/功能/修复)7. 直观类比
pyproject.toml 是产品说明书 + 组装手册合一:上半张([project])告诉 pip"这是什么、依赖什么";下半张([build-system]/[tool.*])告诉机器"怎么打包、怎么测试"。wheel 则是成品快递箱——用户拆箱即用,不用在自己家里现做。
8. 实例与案例
bash
# 发布检查清单(个人库首次上传)
1. uv build && twine check dist/*
2. TestPyPI 先试:twine upload -r testpypi dist/*
3. pip install -i https://test.pypi.org/simple/ mylib 验证
4. 正式 twine upload;打 git tag vX.Y.Z9. 常见误区
- 继续用 setup.py 起新项目 —— 已是遗留路线;新项目一律 pyproject.toml。
- 把依赖钉死(==)写进库的
dependencies—— 库声明下限(>=),应用/lock 文件才钉死版本;否则生态互锁。 - flat 布局导致测试被装进包 —— 测试目录混在包里;用 src 布局天然隔离。
- 本地能装别人装不上 —— 忘了 editable 安装、缺
__init__.py、依赖漏声明;用干净 venv 复测。
10. 自测题
- sdist 与 wheel 的本质区别?pip 优先用哪个?
pip install mylib[cli]中的[cli]对应 pyproject 里哪段配置?- 为什么库不应在 dependencies 里用
==?
参考答案
- sdist 是源码包(用户侧需构建),wheel 是预构建产物(解包即用);pip 优先 wheel。
[project.optional-dependencies]的cli组。- 库之间共享同一解释器环境,钉死版本会与依赖同一库的其他包产生不可解的版本冲突;应用才有"锁定整个环境"的正当性。
11. 与其他知识点的关系
12. 延伸阅读
- Python Packaging User Guide:https://packaging.python.org/
- PEP 621(pyproject 元数据)