使用 pyproject.toml 制作 Python 安装包
Python 库/模块打包(Library Packaging)在过去几年里经历了巨大的变革。随着 PEP 517 / PEP 518 以及 PEP 621 标准的普及,Python 社区已经彻底告别了过去混乱的
setup.py时代,全面拥抱以pyproject.toml为核心的现代化、声明式打包方案。本文将全面总结目前 Python 库打包的核心概念、主流工具方案对比以及标准工作流。
核心概念:现代 Python 打包的基石
在了解具体工具之前,必须先理清现代 Python 打包的几个核心概念:
pyproject.toml:现代 Python 项目的唯一配置入口。取代了过去的setup.py,setup.cfg,requirements.txt,MANIFEST.in等多个文件。- 构建前端 (Build Frontend):负责调用后端的工具,例如
pip、build。它们读取配置并告诉后端去干活。 - 构建后端 (Build Backend):真正执行代码打包的引擎,例如
setuptools,hatchling,poetry-core,flit-core。负责生成分发包。 - 分发格式:
- sdist (Source Distribution):源码包(通常是
.tar.gz),包含源代码和构建元数据。 - wheel (Built Distribution):二进制包(
.whl),一种预编译的格式,安装速度极快,无需在目标机器上执行构建过程。
- sdist (Source Distribution):源码包(通常是
主流打包工具/方案对比
目前 Python 社区有几种主流的构建后端和打包管理工具,它们各有侧重:
Hatch (当前 PyPA 官方力推的新星)
Hatch 是一个现代化的、可扩展的 Python 项目构建与管理工具。Python 官方框架(如 pip 自身、Pytest 等)正在大量迁移至 Hatch。
- 构建后端:
hatchling - 特点:
- 全面支持 PEP 621 标准(标准化的
[project]表)。 - 除了打包,还内置了环境管理、脚本执行、版本号管理(Hatch-vcs)。
- 插件系统极其强大。
- 全面支持 PEP 621 标准(标准化的
- 适用场景:推荐用于所有新项目,特别是纯 Python 库以及需要复杂环境测试的开源库。
Poetry (开发者体验最好的全能战士)
Poetry 将依赖管理和打包发布融为一体,提供了类似 Node.js 中 npm/yarn 的丝滑体验。
- 构建后端:
poetry-core - 特点:
- 拥有强大的依赖解析器和 Lock 文件 (
poetry.lock),确保环境绝对一致。 - 命令极其简洁明了(如
poetry build,poetry publish)。 - 早期使用私有规范
[tool.poetry],但现在也已支持标准的[project]规范。
- 拥有强大的依赖解析器和 Lock 文件 (
- 适用场景:既注重库的打包,又需要极其严谨的依赖管理(如带复杂依赖链的数据科学库或 Web 框架扩展)。
Setuptools (最老牌、最成熟的基石)
过去几十年的绝对霸主。虽然现在推荐用 pyproject.toml,但底层依然可以是 Setuptools。
- 构建后端:
setuptools.build_meta - 特点:
- 对 C/C++/Cython 扩展模块支持最好、最成熟。
- 向后兼容性极强。
- 现在也完全支持通过
pyproject.toml进行声明式配置,无需写setup.py。
- 适用场景:包含复杂的 C/C++ 扩展、Cython 编译的库(如机器学习底层库),或者维护老旧项目。
Flit (最极简的纯 Python 打包器)
Flit 诞生的目的就是为了让打包“纯 Python 库”变得极致简单,没有任何历史包袱。
- 构建后端:
flit_core.buildapi - 特点:
- 配置极简,只需几行代码。
- 不支持 C 扩展,不支持复杂的构建步骤。
- 直接从代码中提取 docstring 作为项目描述,提取
__version__作为版本。
- 适用场景:单文件模块、轻量级的纯 Python 工具包。
PDM (支持 PEP 582 的后起之秀)
与 Poetry 类似,也是一个现代化的包和依赖管理器。
- 构建后端:
pdm-backend - 特点:
- 原生支持 PEP 621 标准。
- 支持 PEP 582(本地
__pypackages__目录,无需虚拟环境,虽然该 PEP 最终被拒绝,但 PDM 依然支持且很好用)。
- 适用场景:喜欢 Poetry 的工作流,但更倾向于拥抱最新 PEP 标准的开发者。
包含非 Python 代码(C/Rust)的特殊方案
如果你的库为了性能需要包含 C/C++ 或 Rust 代码,通常需要特殊的构建后端:
- Maturin / setuptools-rust:用于打包结合了 Rust 的 Python 库(如 Polars、Pydantic v2 就是用 Maturin 打包的)。Maturin 体验极佳。
- Scikit-build-core:替代老旧的
scikit-build,结合 CMake 打包复杂的 C/C++ 扩展库。 - Meson-python:使用 Meson 构建系统的后端,SciPy 和 NumPy 目前正在迁移使用这个方案。
现代标准打包发布工作流 (以 Hatch/Setuptools 为例)
无论您使用哪个后端,纯粹的标准化打包流程如下:
编写配置文件 pyproject.toml
这是必须的。以下是一个现代标准配置示例:
[build-system]
# 选择你的构建后端,这里以 hatchling 为例
requires = [`hatchling`]
build-backend = `hatchling.build`
[project]
name = `my-awesome-lib`
version = `0.1.0`
description = `A short description of the package`
readme = `README.md`
requires-python = `>=3.8`
license = { text = `MIT` }
authors = [
{ name = `Your Name`, email = `your.email@example.com` }
]
# 声明你的库运行时需要的依赖
dependencies = [
`requests>=2.0.0`,
]
[project.scripts]
# 可选:如果你的库包含命令行工具,在这里注册
my-cli = `my_awesome_lib.cli:main`安装构建前端工具 build
build 是 PyPA 官方推荐的构建前端工具,取代了直接运行 python setup.py sdist bdist_wheel。
pip install build执行构建
在包含 pyproject.toml 的目录下运行:
python -m build执行完毕后,会在 dist/ 目录下生成两个文件:
my_awesome_lib-0.1.0.tar.gz(sdist)my_awesome_lib-0.1.0-py3-none-any.whl(wheel)
发布到 PyPI
使用官方推荐的上传工具 twine:
pip install twine
# 先在 TestPyPI 测试
twine upload --repository testpypi dist/*
# 确认无误后上传到正式 PyPI
twine upload dist/*(注:如果您使用的是 Poetry 或 Hatch,它们自带了 poetry build/hatch build 和 poetry publish/hatch publish 命令,可以一键替代上述 2, 3, 4 步。)
总结与选型建议
在 2026+ 年,如何选择适合自己的打包方案?
- 推荐首选:Hatch。它是目前 Python 官方生态的亲儿子,轻量、标准、插件生态好。如果只是想简单开发一个纯 Python 库并发布,用 Hatch 最符合未来趋势。
- 团队协作/重度依赖管理:Poetry 或 PDM。如果你的项目有几十个依赖项,需要严格管控依赖版本以防环境冲突,Poetry 依然是综合体验最好的工具。
- 极简主义者:Flit。对于单个脚本或仅有几个文件的微型库,Flit 是最快的选择。
- 底层性能库 (C/C++):Setuptools 或 Scikit-build-core。
- 底层性能库 (Rust):Maturin。
核心原则: 全面拥抱 pyproject.toml。