本文将详细介绍如何构建并发布一个 Python 包到 Python Package Index (PyPI,可以理解为 Python 的”应用商店”,所有人都可以上传和下载 Python 包),涵盖从手动发布到 CI 自动化发布的完整流程。
📑 目录
- 项目结构规划
- 编写 pyproject.toml
- 安装构建工具
- 本地构建 Python 包
- 手动发布到 PyPI
- 测试安装
- GitHub Actions 自动发布
- PyPI Trusted Publisher 配置
- 进阶:使用 hatch-vcs 自动管理版本
- 完整流程总结
一、项目结构规划
推荐使用 src/ 布局,这是现代 Python 项目的最佳实践:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| my-package/ │ ├── src/ │ └── my_package/ │ ├── __init__.py │ └── hello.py │ ├── tests/ │ └── test_hello.py │ ├── .github/ │ └── workflows/ │ └── publish.yml │ ├── pyproject.toml ├── README.md └── LICENSE
|
示例代码 src/my_package/hello.py:
1 2
| def say_hello(): print("Hello from my package!")
|
src/my_package/__init__.py:
1 2 3
| from my_package.hello import say_hello
__all__ = ["say_hello"]
|
为什么使用 src/ 布局?
- 避免本地模块与安装后的模块混淆
- 强制在测试时使用已安装的包
- 防止意外导入未打包的模块
二、编写 pyproject.toml
pyproject.toml 是 Python 包的核心配置文件(可以把它想象成项目的”身份证”,记录了包名、版本、依赖等所有元信息),遵循 PEP 517/518/621 规范。
2.1 基础配置(使用 uv 构建)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| [build-system] requires = ["uv"] build-backend = "uv.build"
[project] name = "my-package-demo" version = "0.1.0" description = "A demo Python package" readme = "README.md" requires-python = ">=3.9" license = "MIT" authors = [ { name = "Your Name", email = "you@example.com" } ]
dependencies = []
[project.urls] Homepage = "https://github.com/username/my-package" Repository = "https://github.com/username/my-package"
|
2.2 进阶配置(使用 hatchling + hatch-vcs)
使用 hatchling + hatch-vcs 实现更强大的构建和版本管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| [build-system] requires = ["hatch-vcs", "hatchling"] build-backend = "hatchling.build"
[project] name = "my-package" description = "A demo Python package with advanced features" readme = "README.md" requires-python = ">=3.12" license = "MIT" authors = [{ name = "Your Name", email = "you@example.com" }] dependencies = [ "pydantic>=2.0", "typer>=0.15.0", ] dynamic = ["version"]
[project.scripts] my-cli = "my_package.cli:app"
[project.entry-points."my_package.plugins"] default = "my_package.plugins.default:DefaultPlugin"
[project.urls] Homepage = "https://github.com/username/my-package" Repository = "https://github.com/username/my-package"
[tool.hatch.build.targets.wheel] packages = ["src/my_package"]
[tool.hatch.version] source = "vcs"
[tool.hatch.build.hooks.vcs] version-file = "src/my_package/_version.py"
|
2.3 关键字段说明
| 字段 |
作用 |
name |
PyPI 包名(用户 pip install 时使用) |
version |
版本号(或设为 dynamic 自动生成) |
dependencies |
运行时依赖 |
requires-python |
支持的 Python 版本 |
project.scripts |
安装后可用的命令行工具 |
project.entry-points |
插件系统入口点 |
三、安装构建工具
3.1 安装 uv(推荐)
Linux / macOS:
1
| curl -LsSf https://astral.sh/uv/install.sh | sh
|
Windows:
1
| irm https://astral.sh/uv/install.ps1 | iex
|
验证安装:
3.2 安装 hatch(可选)
如果使用 hatchling 构建:
四、本地构建 Python 包
进入项目目录执行构建:
构建成功后会生成 dist/ 目录:
1 2 3
| dist/ ├── my_package_demo-0.1.0.tar.gz # 源码包 └── my_package_demo-0.1.0-py3-none-any.whl # wheel 二进制包
|
| 文件类型 |
说明 |
.tar.gz |
源码分发包(sdist),安装时需要构建 |
.whl |
wheel 包——Python 包的”安装包”格式,已经预编译好,安装时直接解压就行,比 sdist(源码包)快得多 |
五、手动发布到 PyPI
5.1 安装上传工具
5.2 上传到 PyPI
系统会提示输入凭据:
1 2
| username: __token__ password: pypi-xxxx # 在 PyPI 账号中生成的 API Token
|
5.3 获取 PyPI Token
- 登录 https://pypi.org
- 进入 Account Settings → API tokens
- 创建新 Token,选择作用域(整个账号或特定项目)
- 复制 Token(以
pypi- 开头)
发布成功后可在以下地址查看:
1
| https://pypi.org/project/my-package-demo/
|
六、测试安装
从 PyPI 安装并测试:
1
| pip install my-package-demo
|
验证:
1 2 3
| from my_package.hello import say_hello say_hello()
|
七、GitHub Actions 自动发布
手动发布容易出错,生产环境推荐使用 CI 自动化。以下是推荐的配置:
7.1 创建 workflow 文件
.github/workflows/publish.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| name: "CI - Publish"
on: push: tags: ["v*"] workflow_dispatch:
permissions: contents: read id-token: write
jobs: build: name: Build Distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v6
- name: Install uv uses: astral-sh/setup-uv@v7 with: python-version: "3.13"
- name: Build package run: uv build
- name: Upload distribution uses: actions/upload-artifact@v4 with: name: dist path: dist/
publish: name: Publish To PyPI needs: build runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/my-package steps: - name: Download distribution uses: actions/download-artifact@v4 with: name: dist path: dist/
- name: Publish package uses: pypa/gh-action-pypi-publish@release/v1
|
7.2 workflow 结构说明
1 2 3 4 5 6 7 8 9 10 11
| 触发条件 ↓ build job(构建) ├── checkout 代码 ├── 安装 uv ├── uv build 构建包 └── 上传 artifact ↓ publish job(发布) ├── 下载 artifact └── 发布到 PyPI
|
关键设计:
- build 与 publish 分离 - 构建产物通过 artifact 传递,职责清晰
- tag 触发 - 只有推送版本 tag 才触发发布
- OIDC 认证 - 无需存储 PyPI Token
八、PyPI Trusted Publisher 配置
PyPI Trusted Publisher 使用 OIDC(OpenID Connect)认证,无需在 GitHub 存储 API Token,更安全。
8.1 配置步骤
登录 PyPI:https://pypi.org/manage/account/publishing/
点击 “Add a new pending publisher”(新项目)或在项目设置中添加
填写信息:
- Owner: GitHub 用户名或组织名
- Repository name: 仓库名
- Workflow name:
publish.yml(与你的 workflow 文件名一致)
- Environment name:
pypi(与 workflow 中的 environment 一致)
保存配置
8.2 确保 workflow 配置正确
1 2 3 4 5 6 7
| permissions: id-token: write
jobs: publish: environment: name: pypi
|
九、进阶:使用 hatch-vcs 自动管理版本
手动维护版本号容易遗忘或出错。推荐使用 hatch-vcs 从 git tag 自动获取版本。
9.1 配置 pyproject.toml
1 2 3 4 5 6 7 8 9 10 11 12 13
| [build-system] requires = ["hatch-vcs", "hatchling"] build-backend = "hatchling.build"
[project] name = "my-package" dynamic = ["version"]
[tool.hatch.version] source = "vcs"
[tool.hatch.build.hooks.vcs] version-file = "src/my_package/_version.py"
|
9.2 工作流程
1 2 3 4 5 6 7
| git tag v1.2.3 ↓ 构建时自动读取 tag ↓ 版本号设为 1.2.3 ↓ 生成 _version.py 文件
|
优点:
- 版本号与 git tag 强绑定,不会出错
- 无需手动修改代码中的版本号
- 代码中可通过
_version.py 访问版本
十、完整流程总结
10.1 手动发布流程
1 2 3 4 5 6 7 8 9 10 11
| 创建项目结构(src/ 布局) ↓ 编写 pyproject.toml ↓ uv build(构建) ↓ 生成 dist/(.tar.gz + .whl) ↓ twine upload dist/* ↓ 发布到 PyPI
|
10.2 自动发布流程
1 2 3 4 5 6 7 8 9 10 11 12 13
| Developer ↓ git tag v1.0.0 && git push origin v1.0.0 ↓ GitHub Actions 触发 ↓ uv build 构建 ↓ OIDC 认证 ↓ 自动发布到 PyPI ↓ 用户: pip install my-package
|
10.3 推荐实践清单
| 实践 |
说明 |
使用 src/ 布局 |
避免导入混淆 |
| 使用 tag 触发发布 |
版本控制清晰 |
| build 与 publish 分离 |
职责明确,易于调试 |
| 使用 Trusted Publisher |
无需存储 Token,更安全 |
| 使用 hatch-vcs |
自动管理版本号 |
| 添加测试 |
确保发布前质量 |
📋 附录:常用命令速查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| curl -LsSf https://astral.sh/uv/install.sh | sh
uv build
twine upload dist/*
git tag v1.0.0 git push origin v1.0.0
pip install dist/*.whl
pip install my-package-demo
|
🎯 自我检验清单
读完本文后,你应该能做到以下几点:
📚 参考资源