>>>PyPathPython 学习站
首页›进阶篇›kp-036
进阶篇 · Advanced lv.3 进阶 kp-036

打包与发布

前置知识:kp-014、kp-021

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. 前置知识

kp-014(包结构)、kp-021(依赖管理)。

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.Z

9. 常见误区

  1. 继续用 setup.py 起新项目 —— 已是遗留路线;新项目一律 pyproject.toml。
  2. 把依赖钉死(==)写进库的 dependencies —— 库声明下限(>=),应用/lock 文件才钉死版本;否则生态互锁。
  3. flat 布局导致测试被装进包 —— 测试目录混在包里;用 src 布局天然隔离。
  4. 本地能装别人装不上 —— 忘了 editable 安装、缺 __init__.py、依赖漏声明;用干净 venv 复测。

10. 自测题

  1. sdist 与 wheel 的本质区别?pip 优先用哪个?
  2. pip install mylib[cli] 中的 [cli] 对应 pyproject 里哪段配置?
  3. 为什么库不应在 dependencies 里用 ==?
参考答案
  1. sdist 是源码包(用户侧需构建),wheel 是预构建产物(解包即用);pip 优先 wheel。
  2. [project.optional-dependencies] 的 cli 组。
  3. 库之间共享同一解释器环境,钉死版本会与依赖同一库的其他包产生不可解的版本冲突;应用才有"锁定整个环境"的正当性。

11. 与其他知识点的关系

  • kp-043 自动化与 CLI:把库变成 pipx/uv tool 可安装的命令行工具。
  • kp-035 测试:发布前 CI 的必经关卡。
  • kp-045 工程规范:工具配置的统一住址。

12. 延伸阅读

  • Python Packaging User Guide:https://packaging.python.org/
  • PEP 621(pyproject 元数据)