Python打包之pyproject.toml全方位使用指南
作者:半截的诗_218
前言
在我们的系列文章中,我们已经深入探讨了 setup.py 和 conda-build 这两种强大的打包工具。您可能已经注意到,社区正在逐步从使用 setup.py 这个可执行的 Python 脚本,转向一个名为 pyproject.toml 的静态配置文件。
这不仅仅是一个简单的文件格式变化,它代表了 Python 打包理念的一次重要演进。为什么需要这个新文件?它又该如何使用呢?
本文将为您全面解析 pyproject.toml,这个被 PEP 518 和 PEP 621 等提案确立为官方标准的现代化打包工具。
一、 开发者篇:使用pyproject.toml定义你的项目
作为项目的创建者,拥抱 pyproject.toml 能让你的项目配置更清晰、更标准,并能将以往散落在各种文件(setup.py, setup.cfg, MANIFEST.in 等)中的配置统一起来。
1. 为什么要使用pyproject.toml?
- 标准化:它是官方指定的、统一的 Python 项目配置文件格式。
- 声明式,非可执行:与
setup.py不同,toml文件是静态的,只声明“是什么”,而不是执行“做什么”。这使得构建过程更安全、更可预测。 - 统一配置入口:它不仅能定义项目元数据和依赖,还能统一存放
black,pytest,ruff,mypy等开发工具的配置,让项目根目录更整洁。 - 构建系统解耦:它允许你自由选择构建后端(如
setuptools,poetry,pdm,hatch),而不仅仅是绑定setuptools。
2. 如何创建pyproject.toml?
pyproject.toml 是一个基于 TOML (Tom’s Obvious, Minimal Language) 语法的文本文件,您需要在项目根目录手动创建它。
项目结构示例:
my_modern_project/ ├── my_awesome_tool/ │ ├── __init__.py │ └── main.py ├── tests/ │ └── test_main.py └── pyproject.toml <-- 我们要创建的文件
pyproject.toml 文件详解:
一个遵循 PEP 621 标准的现代化 pyproject.toml 核心内容如下:
# pyproject.toml
# 1. 构建系统声明 (必需)
# 告诉 pip 等工具,这个项目需要用什么来构建 (这里是 setuptools)
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
# 2. 项目核心元数据 (必需)
# 这部分取代了 setup.py 中的大部分参数
[project]
name = "my-awesome-tool"
version = "0.1.0"
authors = [
{ name="你的名字", email="你的邮箱@example.com" },
]
description = "一个功能强大的现代工具"
readme = "README.md" # 指定 README 文件
requires-python = ">=3.9"
license = { text = "MIT" }
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
# 3. 依赖项 (核心)
# 这取代了 setup.py 中的 install_requires
dependencies = [
"requests>=2.20",
"numpy",
"click",
]
# 4. 命令行工具 (可选)
# 这取代了 setup.py 中的 entry_points
[project.scripts]
awesome_tool = "my_awesome_tool.main:cli"
# 5. 可选依赖 (可选)
# 允许用户安装额外功能,例如 pip install "my-awesome-tool[extra]"
[project.optional-dependencies]
extra = ["pandas"]
# 6. 项目链接 (可选)
[project.urls]
Homepage = "https://github.com/your_username/my_modern_project"
"Bug Tracker" = "https://github.com/your_username/my_modern_project/issues"
可以看到,它的结构清晰、可读性强,并且几乎涵盖了 setup.py 的所有核心功能。
3. 开发和安装
开发流程与 setup.py 完全相同。您依然需要在一个虚拟环境中进行开发,并使用 pip 以可编辑模式安装:
# 在激活虚拟环境后,于项目根目录运行 pip install -e .
pip (较新版本) 会自动识别 pyproject.toml 文件,并根据其中的配置来正确安装项目及其依赖。
二、 用户篇:如何使用pyproject.toml定义的项目
作为用户,你可能根本感觉不到任何区别,这正是 pyproject.toml 标准化的美妙之处。
1. 安装流程
当你从 GitHub 下载了一个包含 pyproject.toml 的项目后,安装步骤和传统项目一模一样。
- 创建并激活虚拟环境 (依然是最佳实践)。
- 安装项目:
- 作为普通用户使用:
pip install . - 作为贡献者开发:
pip install -e .
- 作为普通用户使用:
pip 会在后台自动处理所有事情。它会读取 pyproject.toml,理解项目的构建需求和依赖,然后下载并安装所有需要的东西。用户无需关心底层是用 setuptools 还是 poetry 构建的。
三、 总结与优势
pyproject.toml 是 Python 打包的未来,它的优势非常明显:
| 特性 | setup.py (传统方式) | pyproject.toml (现代方式) |
|---|---|---|
| 文件格式 | Python 脚本,可执行 | TOML 配置文件,声明式 |
| 标准性 | 事实标准,但配置分散 | 官方标准 (PEP 518/621) |
| 配置 | 配置分散在 setup.py, setup.cfg 等多个文件中 | 集中管理,可包含项目元数据、依赖和各种开发工具的配置 |
| 安全性 | 执行任意代码有潜在风险 | 静态文件,更安全,构建过程更可预测 |
| 灵活性 | 强绑定 setuptools | 可自由选择构建后端 (setuptools, poetry, pdm, hatch 等) |
四、 那如果项目文件里既有setup.py又有pyproject.toml呢?
pyproject.toml 是现代 Python 打包流程的唯一入口,它的优先级更高。
当一个构建工具(例如 pip)开始工作时,它会首先寻找 pyproject.toml。这个文件决定了接下来该做什么,以及由谁(哪个构建后端)来做。
下面我们来详细分解一下具体的读取和处理逻辑。
读取流程:三步走
入口 (pyproject.toml 的 [build-system] 部分)
pip首先打开pyproject.toml,并且只读取[build-system]这个表格。- 这个表格告诉
pip:“嘿,要构建这个项目,你需要使用setuptools(或者poetry,pdm等)这个工具。” pip会确保这个构建工具被安装,然后把控制权完全交给它。
构建后端接管 (例如 setuptools)
- 现在,
setuptools(作为构建后端) 开始工作。它的任务是收集项目的所有元数据(包名、版本、依赖等)。 setuptools会再次查看pyproject.toml文件,寻找一个名为[project]的表格。
- 现在,
元数据读取的优先级
- 这就是决定性的步骤,会根据文件内容分为以下几种情况:
两种主要场景
场景一:现代项目 (最佳实践)
- 文件状态:
pyproject.toml:包含了完整的[project]表格,里面定义了name,version,dependencies等所有核心信息。setup.py:要么不存在,要么是一个非常简单的“垫片”文件,内容可能只有:# setup.py (shim) from setuptools import setup setup()
- 读取方式:
setuptools会优先读取 pyproject.toml 中的 [project] 表格作为所有元数据的“唯一真实来源”。- 它会忽略
setup.py文件中的任何元数据(如果有的话)。 - 那个简单的
setup.py文件仅仅是为了兼容一些非常老旧的、不认识pyproject.toml的工具。
结论:在这种模式下,pyproject.toml 完胜。
场景二:过渡期项目 (非常普遍)
- 文件状态:
pyproject.toml:只包含了[build-system]表格,可能还有一些工具配置(如[tool.pytest]),但没有 [project] 表格。setup.py:包含一个完整的 setup() 函数,里面定义了name,version,install_requires等所有信息。
- 读取方式:
pip仍然从pyproject.toml开始,得知需要使用setuptools。setuptools接管后,在pyproject.toml中找不到 [project] 表格。- 于是,
setuptools会回退 (fallback) 到它的传统模式:去执行setup.py文件,并从setup()函数的参数中提取所有的项目元数据。
结论:在这种模式下,pyproject.toml 扮演“指路人”的角色,而 setup.py 仍然是“信息提供者”。
总结与建议
| 场景 | pyproject.toml 内容 | setup.py 内容 | 读取优先级 / 谁说了算? |
|---|---|---|---|
| 现代 | 包含 [build-system] 和 [project] | 不存在或仅为简单垫片 | pyproject.toml 说了算。元数据从 [project] 表格读取。 |
| 过渡 | 仅包含 [build-system] | 包含完整的 setup() 函数 | setup.py 说了算。pyproject.toml 仅用于指定构建工具。 |
| 冲突 (应避免) | [project] 和 setup() 都定义了元数据 | [project] 和 setup() 都定义了元数据 | pyproject.toml 说了算。setuptools 会优先采用 [project] 表格中的值。 |
如果 pyproject.toml 文件中包含了完整的 [project] 表格(定义了项目名称、版本、依赖等),同时又存在一个完备的 setup.py 文件,那么现代化的构建工具(如新版 pip 和 setuptools)会:默认优先使用 pyproject.toml 文件中的信息。
pyproject.toml 是官方指定的“新版蓝图”,而 setup.py 是“旧版蓝图”。当两种蓝图都在时,构建系统会默认遵循更新、更标准的那一份。旧的 setup.py 文件中的配置在很大程度上会被忽略,以避免冲突和混淆。
到此这篇关于Python打包之pyproject.toml全方位使用的文章就介绍到这了,更多相关Python打包pyproject.toml内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
