构建 Python 包:从开发到 PyPI 的指南
从代码到 PyPI 部署,掌握 Python 包管理
Python打包 已经有了显著的发展,现代工具和标准使得分发你的代码比以往任何时候都更容易。
本指南将带你逐步构建专业的Python包并将其发布到PyPI。如果你是Python新手,或者需要快速参考,请查看我们的Python速查表,以快速掌握Python基础知识。

为什么打包你的Python代码?
打包你的Python项目有多个好处:
- 可重用性:无需复制粘贴,即可在多个项目中共享代码
- 分发:通过简单的
pip install,让其他人安装你的代码 - 依赖管理:明确指定并管理依赖项
- 版本控制:跟踪发布版本并维护向后兼容性
- 专业标准:遵循Python社区的最佳实践
- 文档:结构鼓励适当的文档和测试
现代Python包结构
一个组织良好的包遵循以下结构:
my-package/
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ └── test_core.py
├── docs/
│ └── index.md
├── .github/
│ └── workflows/
│ └── publish.yml
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore
src-layout 现在比扁平布局更受推荐,因为它:
- 防止在开发过程中意外导入未安装的代码
- 通过强制安装使测试更加可靠
- 清晰地将源代码与其他项目文件分开
在组织包的内部结构时,考虑应用干净的架构原则,以使你的代码更易于维护和测试。我们的指南Python清洁架构设计模式涵盖了与Python包配合出色的SOLID原则、依赖注入和分层架构模式。
pyproject.toml 文件:现代配置
pyproject.toml (PEP 518, 621) 是Python项目配置的现代标准,取代了传统的 setup.py:
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mypackage"
version = "0.1.0"
description = "一个出色的Python包"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "你的名字", email = "you@example.com"}
]
keywords = ["示例", "教程", "包"]
classifiers = [
"开发状态 :: 4 - 测试版",
"目标受众 :: 开发者",
"许可证 :: OSI 批准 :: MIT 许可证",
"编程语言 :: Python :: 3",
"编程语言 :: Python :: 3.8",
"编程语言 :: Python :: 3.9",
"编程语言 :: Python :: 3.10",
"编程语言 :: Python :: 3.11",
"编程语言 :: Python :: 3.12",
]
dependencies = [
"requests>=2.28.0",
"click>=8.1.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.4.0",
"black>=23.0.0",
"flake8>=6.0.0",
"mypy>=1.5.0",
]
docs = [
"sphinx>=7.0.0",
"sphinx-rtd-theme>=1.3.0",
]
[project.urls]
Homepage = "https://github.com/yourusername/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/yourusername/mypackage"
Issues = "https://github.com/yourusername/mypackage/issues"
[project.scripts]
mypackage-cli = "mypackage.cli:main"
[tool.setuptools.packages.find]
where = ["src"]
关键配置部分
- build-system:指定构建后端(setuptools、hatchling、poetry-core、flit-core)
- project:核心元数据和依赖项
- project.optional-dependencies:额外功能依赖项(开发、文档、测试)
- project.scripts:命令行入口点
- tool.*:工具特定配置(black、pytest、mypy 等)
选择构建后端
Setuptools(标准选择)
最广泛使用且兼容的选项:
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
优点:通用兼容性、功能丰富、生态系统庞大 缺点:配置更冗长、比新选项慢
Hatchling(现代且快速)
轻量且高性能的现代后端:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
优点:快速构建、简单配置、良好的默认值 缺点:插件比setuptools少
Poetry(一站式)
完整的依赖和环境管理:
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "一个出色的Python包"
authors = ["你的名字 <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.28.0"
优点:依赖解析、锁定文件、集成工作流程 缺点:不同的工作流程、较大的工具表面区域
Flit(极简)
用于简单包的简单工具:
[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"
优点:极其简单、最小配置 缺点:对复杂包功能有限
构建你的包
安装构建工具
# 安装现代构建工具
pip install build
# 或者使用Poetry
pip install poetry
# 或者使用Hatchling
pip install hatch
构建发行文件
使用 build 包(适用于任何后端):
# 清除之前的构建
rm -rf dist/ build/ *.egg-info
# 构建源代码发行版和wheel
python -m build
# 这会创建:
# dist/mypackage-0.1.0.tar.gz (源代码发行版)
# dist/mypackage-0.1.0-py3-none-any.whl (wheel)
使用Poetry:
poetry build
使用Hatch:
hatch build
理解发行格式
源代码发行版 (sdist) - .tar.gz
- 包含源代码和构建说明
- 用户的 pip 在安装时构建它
- 包含测试、文档和其他开发文件
Wheel - .whl
- 预构建的二进制发行版
- 安装速度快(无构建步骤)
- 平台特定或纯Python
- 推荐的发行格式
发布到PyPI
方法1:使用Twine手动上传(传统方法)
# 安装twine
pip install twine
# 上传前检查包
twine check dist/*
# 首先上传到TestPyPI(推荐)
twine upload --repository testpypi dist/*
# 从TestPyPI测试安装
pip install --index-url https://test.pypi.org/simple/ mypackage
# 上传到生产PyPI
twine upload dist/*
在 ~/.pypirc 中配置凭据:
[distutils]
index-servers =
pypi
testpypi
[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...
[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgENdGVzdC5weXBpLm9yZw...
方法2:信任发布(推荐用于CI/CD)
信任发布使用OpenID Connect (OIDC) 从CI/CD平台进行身份验证,无需存储令牌。
在PyPI中设置:
- 前往你的PyPI项目设置
- 导航到“发布”部分
- 添加一个新的“待发布者”
- 配置:
- 所有者:你的GitHub用户名/组织
- 仓库:仓库名称
- 工作流程:
publish.yml - 环境:
release(可选但推荐)
GitHub Actions工作流程(.github/workflows/publish.yml):
name: 发布到PyPI
on:
release:
types: [published]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 设置Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 安装构建依赖项
run: |
python -m pip install --upgrade pip
pip install build
- name: 构建包
run: python -m build
- name: 存储发行包
uses: actions/upload-artifact@v3
with:
name: python-package-distributions
path: dist/
publish:
needs: build
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
steps:
- name: 下载发行包
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: 发布到PyPI
uses: pypa/gh-action-pypi-publish@release/v1
优点:
- 无需管理或保护API令牌
- 通过OIDC自动认证
- 通过环境保护规则增强安全性
- 所有发布都有审计跟踪
最佳实践
1. 版本管理
使用语义版本控制(SemVer):MAJOR.MINOR.PATCH
# 安装版本提升工具
pip install bump2version
# 提升版本
bump2version patch # 0.1.0 -> 0.1.1
bump2version minor # 0.1.1 -> 0.2.0
bump2version major # 0.2.0 -> 1.0.0
在 .bumpversion.cfg 中配置:
[bumpversion]
current_version = 0.1.0
commit = True
tag = True
[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"
[bumpversion:file:src/mypackage/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"
2. 包含必要的文件
README.md:清晰的项目描述、安装、使用示例 LICENSE:选择适当的许可证(MIT、Apache 2.0、GPL 等) CHANGELOG.md:记录版本之间的更改 .gitignore:排除构建产物、缓存、虚拟环境
3. 全面的测试
# 安装测试依赖项
pip install pytest pytest-cov
# 带覆盖率运行测试
pytest --cov=mypackage tests/
# 生成覆盖率报告
pytest --cov=mypackage --cov-report=html tests/
在 pyproject.toml 中配置:
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "--strict-markers --cov=mypackage"
4. 代码质量工具
# 格式化
black src/ tests/
# 检查代码风格
flake8 src/ tests/
# 类型检查
mypy src/
# 导入排序
isort src/ tests/
将它们集成到pre-commit钩子中(.pre-commit-config.yaml):
repos:
- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 6.1.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
hooks:
- id: mypy
5. 文档
使用Sphinx进行文档编写:
# 安装Sphinx
pip install sphinx sphinx-rtd-theme
# 初始化文档
cd docs
sphinx-quickstart
# 构建文档
make html
或使用MkDocs进行更简单的Markdown文档:
pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve
6. 持续集成
在不同平台和环境中测试你的Python包对于可靠性至关重要。关于Python在不同部署场景中的性能,参见我们的比较文章 AWS Lambda性能:JavaScript vs Python vs Golang,该文章探讨了Python在无服务器环境中的性能表现。
完整的CI/CD工作流程(.github/workflows/ci.yml):
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: 设置Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: 安装依赖项
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: 运行测试
run: pytest --cov=mypackage --cov-report=xml
- name: 上传覆盖率
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 设置Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 安装依赖项
run: |
pip install black flake8 mypy
- name: Black格式检查
run: black --check src/ tests/
- name: Flake8
run: flake8 src/ tests/
- name: MyPy
run: mypy src/
常见问题及解决方案
问题1:安装后导入错误
问题:包已安装但导入失败
解决方案:确保使用 __init__.py 文件和正确的 pyproject.toml 配置进行正确的包结构:
[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]
问题2:缺少依赖项
问题:包安装后由于缺少依赖项而在运行时失败
解决方案:在 pyproject.toml 中声明所有运行时依赖项:
[project]
dependencies = [
"requests>=2.28.0",
"click>=8.1.0",
]
问题3:版本冲突
问题:包在开发中工作但在生产中失败
解决方案:使用虚拟环境并指定最低版本:
# 创建隔离环境
python -m venv .venv
source .venv/bin/activate # 在Windows上:.venv\Scripts\activate
# 以可编辑模式安装用于开发
pip install -e ".[dev]"
问题4:包体积过大
问题:包下载/安装时间过长
解决方案:使用 MANIFEST.in 排除不必要的文件:
include README.md
include LICENSE
include pyproject.toml
recursive-include src *.py
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-exclude tests *
recursive-exclude docs *
问题5:平台特定问题
问题:包在一个操作系统上工作但在另一个上失败
解决方案:使用CI/CD矩阵构建在多个平台上进行测试(参见上面的CI示例)
发布检查清单
在将包发布到PyPI之前,请验证以下内容:
- 所有测试在Python版本上通过
- 代码已格式化并经过检查
- README.md 完整并包含示例
- 包含LICENSE文件
- 版本号已更新
- CHANGELOG.md 已更新
- 依赖项已正确指定
- 包构建无错误 (
python -m build) - 在TestPyPI上测试了包
- 文档已更新
- Git仓库已用版本标记
- 创建了GitHub发布(用于信任发布)
高级主题
入口点和CLI工具
创建命令行接口:
[project.scripts]
mypackage = "mypackage.cli:main"
在 src/mypackage/cli.py 中的实现:
import click
@click.command()
@click.option('--name', default='World', help='要问候的名字')
def main(name):
"""简单的CLI工具示例"""
click.echo(f'Hello, {name}!')
if __name__ == '__main__':
main()
对于创建与外部API交互的Python包的现实示例,请参阅我们的指南 将Ollama与Python集成,该指南演示了使用REST API和官方库集成构建Python客户端。
插件系统
启用插件发现:
[project.entry-points."mypackage.plugins"]
plugin_a = "mypackage_plugin_a:PluginA"
C扩展
对于性能关键代码:
[tool.setuptools.ext-modules]
name = "mypackage._speedups"
sources = ["src/mypackage/_speedups.c"]
命名空间包
对于在共同命名空间下的分布式包:
[tool.setuptools.packages.find]
where = ["src"]
include = ["company.*"]
[tool.setuptools.package-data]
"company.mypackage" = ["py.typed"]
有用的工具和资源
包开发工具
- cookiecutter:Python包的项目模板
- tox:跨Python版本的测试自动化
- nox:灵活的测试自动化(tox的替代)
- pre-commit:代码质量的Git钩子框架
- commitizen:标准化提交信息和版本控制
文档平台
- Read the Docs:免费文档托管
- GitHub Pages:托管MkDocs或Sphinx文档
- pdoc:简单的API文档生成器
包注册表
- PyPI:官方Python包索引(pypi.org)
- TestPyPI:测试环境(test.pypi.org)
- Anaconda.org:Conda包分发
- GitHub Packages:私有包托管
监控和分析
- PyPI Stats:包的下载统计
- Libraries.io:依赖监控和警报
- Snyk:安全漏洞扫描
- Dependabot:自动依赖更新
结论
现代Python打包已经发展为更加开发者友好,采用如 pyproject.toml 的标准、强大的构建后端以及通过信任发布进行安全发布。遵循本指南,你可以创建专业、可维护的Python包,有效地服务于你的用户。
关键要点:
- 所有新项目使用 pyproject.toml
- 根据需求选择合适的 构建后端
- 实施 自动化测试 和CI/CD
- 使用 信任发布 进行安全、无令牌的部署
- 遵循 语义版本控制 并维护变更日志
- 提供全面的 文档 和示例
Python打包生态系统继续改进,具有更好的工具、标准和安全功能。保持更新Python增强提案(PEPs)和社区最佳实践,以确保你的包现代且可维护。
有用的链接
- Python打包用户指南
- PEP 518 - 指定构建系统需求
- PEP 621 - 在pyproject.toml中存储项目元数据
- PyPI信任发布文档
- Setuptools文档
- Poetry文档
- Hatch文档
- TestPyPI
- Python打包权威GitHub
- 语义版本控制
- 选择许可证