Python 打包发布:wheel、sdist 与 PyPI 上传

FreeGuideOnline 最新 2026-06-16

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.pysetup.cfg

旧版教程中常见的 setup.pysetup.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

该命令会:

  1. dist/ 目录下生成一个 .tar.gz 文件(sdist)。
  2. 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。

  1. 登录 PyPI,进入 Account Settings → API tokens。
  2. 选择 “Add API token”,创建一个具有上传权限的 token(范围选择 “Entire account” 或项目限定)。
  3. 复制生成的 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 或添加功能后,需要:

  1. 修改 pyproject.toml 中的 version 字段,例如从 0.1.0 改为 0.1.10.2.0。PyPI 要求每次上传的版本号必须唯一。
  2. 重新运行 python -m build 生成新的分发包。
  3. 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 社区吧!