Python 打包发布:wheel、sdist 与 PyPI 上传
Python 打包发布:wheel、sdist 与 PyPI 上传
将你的 Python 项目打包并发布到 PyPI(Python Package Index),是让全世界开发者能够通过 pip install 轻松复用你代码的关键一步。本教程将从零开始,带你掌握现代 Python 打包的标准流程:如何构建源码分发包(sdist)和 wheel,以及如何使用 Twine 上传至 PyPI。
项目结构准备
在开始打包之前,先确保你的项目遵循标准的目录结构。假设你要发布的包名为 mypackage,推荐的结构如下:
mypackage/
├── src/
│ └── mypackage/
│ ├── __init__.py
│ └── core.py
├── tests/
│ └── test_core.py
├── pyproject.toml
├── README.md
└── LICENSE
关键点:
src/布局(推荐):将所有包代码放在src/目录下,可以避免导入时意外使用开发目录中的代码。__init__.py:让 Python 将该目录识别为包,可以为空。pyproject.toml:现代 Python 项目的配置文件,集中管理构建系统、项目元数据和工具设置。README.md:项目说明文档,会自动显示在 PyPI 页面上。LICENSE:明确许可证,打包时会被包含进去。
配置项目元数据
Python 打包需要告诉构建工具你的包叫什么、版本是多少、依赖哪些库。这些信息全部写在 pyproject.toml 中。
最小可用的 pyproject.toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mypackage"
version = "0.1.0"
authors = [
{ name="你的名字", email="your@email.com" },
]
description = "一个示例 Python 包"
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
]
[project.urls]
Homepage = "https://github.com/yourusername/mypackage"
Issues = "https://github.com/yourusername/mypackage/issues"
[build-system]:声明使用 setuptools 作为构建后端,并指定 wheel 依赖。[project]:包含 PyPI 显示所需的核心元数据。version遵循语义化版本。requires-python限定了 Python 版本。classifiers:PyPI 的分类标签,可从 pypi.org/classifiers 选择。[project.urls]:任意相关链接,方便用户找到源码仓库等。
更详细的依赖可以添加 dependencies 字段:
[project]
# ...
dependencies = [
"requests>=2.28.0",
"click>=8.0",
]
如果还有开发依赖(测试、格式化工等),可写在 [project.optional-dependencies] 中:
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
]
关于 setup.py 和 setup.cfg
旧版教程中常见的 setup.py 和 setup.cfg 已被 pyproject.toml 逐步取代。setuptools 从 61.0 版本起完全支持从 pyproject.toml 读取配置,因此不再需要单独的 setup.py,除非你有非常复杂的自定义构建逻辑。
构建分发包:sdist 与 wheel
Python 分发包主要有两种形式:
- sdist(Source Distribution):源码分发,包含原始代码和
pyproject.toml,安装时需要构建。 - wheel(Built Distribution):预构建的
.whl文件,安装快,不依赖构建系统,是推荐的安装方式。
安装构建工具
首先确保你的 Python 环境中有最新的构建工具:
pip install --upgrade build twine
- build:PEP 517 标准构建前端,负责生成 sdist 和 wheel。
- twine:用于安全上传分发包到 PyPI。
生成分发包
在项目根目录(mypackage/,包含 pyproject.toml 的目录)下运行:
python -m build
该命令会:
- 在
dist/目录下生成一个.tar.gz文件(sdist)。 - 在
dist/目录下生成一个.whl文件(wheel)。
输出类似:
dist/
├── mypackage-0.1.0.tar.gz
└── mypackage-0.1.0-py3-none-any.whl
- tar.gz 文件:包含完整的源码,任何平台安装时都可以用。
- whl 文件:文件名编码了兼容性信息:
py3表示 Python 3 任意版本,none表示 ABI 无依赖,any表示任何平台。如果你的包包含 C 扩展,文件名会有所不同。
两种分发包的作用
- 上传到 PyPI 时,最好同时提供 wheel 和 sdist,这样绝大部分用户都能获得快速安装体验,少数特殊平台也能通过 sdist 从源码构建。
- 如果只上传 wheel,某些平台可能无法安装;只上传 sdist,每次安装都必须构建,影响体验。
发布到 PyPI
注册 PyPI 账号
前往 https://pypi.org 注册账号,并验证邮箱。正式发布前,强烈建议先在 Test PyPI(https://test.pypi.org)上进行测试。两个站点账号独立,需分别注册。
配置 API Token(推荐)
不想每次上传都输入密码,可以使用 API Token。
- 登录 PyPI,进入 Account Settings → API tokens。
- 选择 “Add API token”,创建一个具有上传权限的 token(范围选择 “Entire account” 或项目限定)。
- 复制生成的 token,它只会显示一次。
你可以将 token 保存在 $HOME/.pypirc 文件中:
[pypi]
username = __token__
password = pypi-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
对于 Test PyPI,重复同样操作,并将配置写在 [testpypi] 段。
使用 Twine 上传
构建完成后,使用 Twine 将 dist/ 中的所有文件上传:
twine upload dist/*
Twine 会自动提示你选择 PyPI 仓库(或通过 --repository 指定)。如果配置了 .pypirc 的 token,它会使用 token 认证。
如果只想上传到 Test PyPI:
twine upload --repository testpypi dist/*
上传成功后,你可以在相应 PyPI 网站上看到你的包。
从 Test PyPI 安装验证
在另一个干净的环境中,测试从 Test PyPI 安装:
pip install --index-url https://test.pypi.org/simple/ mypackage
确保导入和使用正常后,再正式上传到主 PyPI。
更新版本与再次发布
当你修复 bug 或添加功能后,需要:
- 修改
pyproject.toml中的version字段,例如从0.1.0改为0.1.1或0.2.0。PyPI 要求每次上传的版本号必须唯一。 - 重新运行
python -m build生成新的分发包。 - 用
twine upload dist/*上传。
提示:每次构建前,可以删除旧的
dist/目录,避免不小心上传旧版本。
最佳实践总结
- 使用
src布局:避免意外导入开发中的代码。 - 在
pyproject.toml中管理一切:替代setup.py/setup.cfg。 - 同时生成 wheel 和 sdist:保证安装速度和兼容性。
- 先发布到 Test PyPI 验证:防止不可逆的错误。
- 妥善管理版本号:遵循语义化版本,发布前打 Git 标签。
- 包含有意义的 README 和 LICENSE:提升包的可用性和合规性。
- 利用
twine check:上传前检查包描述在 PyPI 上是否能正确渲染:twine check dist/*。
现在你已掌握 Python 打包发布的全流程。去把你的优秀代码贡献给 Python 社区吧!