编写你的 pyproject.toml¶
pyproject.toml 是一个由打包工具以及其他工具(如 linters、类型检查器等)使用的配置文件。此文件中有三个可能的 TOML 表。
强烈建议使用
[build-system]表。它允许你声明你使用的 构建后端 以及构建项目所需的其他依赖项。[project]表是大多数构建后端用来指定项目基本元数据(如依赖项、你的姓名等)的格式。[tool]表包含工具特定的子表,例如[tool.hatch]、[tool.black]、[tool.mypy]。我们在此仅简要介绍此表,因为其内容由每个工具定义。请查阅特定工具的文档以了解其可包含的内容。
注意
无论你使用哪个构建后端,[build-system] 表都应始终存在([build-system] 定义了你使用的构建工具)。
另一方面,[project] 表被大多数构建后端所理解,但有些构建后端使用不同的格式。
一个值得注意的例外是 Poetry,它在 2.0 版本(2025 年 1 月 5 日发布)之前不使用 [project] 表,而是使用 [tool.poetry] 表。从 2.0 版本开始,它同时支持两者。此外,setuptools 构建后端支持 [project] 表,以及 setup.cfg 或 setup.py 中的旧格式。
对于新项目,请使用 [project] 表,并且只有在需要某些程序化配置(例如构建 C 扩展)时才保留 setup.py,但 setup.cfg 和 setup.py 格式仍然有效。请参阅 setup.py 是否已弃用?。
声明构建后端¶
[build-system] 表包含一个 build-backend 键,它指定要使用的构建后端。它还包含一个 requires 键,它是一个构建项目所需的依赖项列表——这通常只是构建后端包,但也可能包含额外的依赖项。你还可以约束版本,例如 requires = ["setuptools >= 61.0"]。
通常,你只需复制你的构建后端文档建议的内容(在 选择你的构建后端 之后)。以下是一些常见构建后端的值
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"
[build-system]
requires = ["setuptools >= 77.0.3"]
build-backend = "setuptools.build_meta"
[build-system]
requires = ["flit_core >= 3.12.0, <4"]
build-backend = "flit_core.buildapi"
[build-system]
requires = ["pdm-backend >= 2.4.0"]
build-backend = "pdm.backend"
[build-system]
requires = ["uv_build >= 0.9.2, <0.10.0"]
build-backend = "uv_build"
静态与动态元数据¶
本指南的其余部分将致力于 [project] 表。
大多数情况下,你将直接写入 [project] 字段的值。例如:requires-python = ">= 3.8",或 version = "1.0"。
然而,在某些情况下,让你的构建后端为你计算元数据是很有用的。例如:许多构建后端可以从你的代码中的 __version__ 属性、Git 标签或类似的地方读取版本。在这种情况下,你应该使用例如以下方式将字段标记为动态的:
[project]
dynamic = ["version"]
当一个字段是动态的,由构建后端负责填充它。请查阅你的构建后端文档以了解其如何实现。
基本信息¶
name¶
在 PyPI 上填写你的项目名称。此字段为必填项,并且是唯一不能标记为动态的字段。
[project]
name = "spam-eggs"
项目名称必须由 ASCII 字母、数字、下划线 “_”、连字符 “-” 和句点 “.” 组成。它不能以下划线、连字符或句点开头或结尾。
项目名称的比较不区分大小写,并认为任意长度的下划线、连字符和/或句点序列是相等的。例如,如果你注册了一个名为 cool-stuff 的项目,用户可以使用以下任何拼写下载它或声明对其的依赖:Cool-Stuff、cool.stuff、COOL_STUFF、CoOl__-.-__sTuFF。
version¶
填写你的项目版本。
[project]
version = "2020.0.0"
一些更复杂的版本说明符是可能的,例如 2020.0.0a1(用于 Alpha 发布);请参阅 规范 获取完整详细信息。
此字段是必需的,尽管它通常被标记为动态,使用
[project]
dynamic = ["version"]
这允许使用从 __version__ 属性或 Git 标签填充版本等用例。有关更多详细信息,请参阅 项目版本的单一来源 讨论。
依赖项和要求¶
dependencies/optional-dependencies¶
如果你的项目有依赖项,请像这样列出它们
[project]
dependencies = [
"httpx",
"gidgethub[httpx]>4.0.0",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'",
]
请参阅 依赖项说明符 以了解可用于约束版本的完整语法。
如果某些依赖项仅在你包的特定功能需要时才需要,你可能希望将它们设为可选。在这种情况下,将它们放在 optional-dependencies 中。
[project.optional-dependencies]
gui = ["PyQt5"]
cli = [
"rich",
"click",
]
每个键定义一个“打包额外项”。在上面的示例中,可以使用例如 pip install your-project-name[gui] 来安装你的项目并支持 GUI,从而添加 PyQt5 依赖项。
requires-python¶
这允许你声明你支持的最低 Python 版本 [1]。
[project]
requires-python = ">= 3.8"
创建可执行脚本¶
要将命令作为包的一部分安装,请在 [project.scripts] 表中声明它。
[project.scripts]
spam-cli = "spam:main_cli"
在此示例中,安装你的项目后,将提供 spam-cli 命令。执行此命令将等同于 import sys; from spam import main_cli; sys.exit(main_cli())。
在 Windows 上,以这种方式打包的脚本需要一个终端,因此如果你从图形应用程序中启动它们,它们将弹出一个终端。为防止这种情况发生,请改用 [project.gui-scripts] 表而不是 [project.scripts]。
[project.gui-scripts]
spam-gui = "spam:main_gui"
在这种情况下,从命令行启动脚本将立即返回控制权,让脚本在后台运行。
[project.scripts] 和 [project.gui-scripts] 之间的区别仅在 Windows 上相关。
关于你的项目¶
description¶
这应该是一个项目的一行描述,在 PyPI 上的项目页面(示例)和其他地方(如搜索结果列表(示例))显示为项目的“标题”。
[project]
description = "Lovely Spam! Wonderful Spam!"
readme¶
这是你的项目更长的描述,用于显示在 PyPI 上的项目页面。通常,你的项目将有一个 README.md 或 README.rst 文件,你只需在此处填写其文件名。
[project]
readme = "README.md"
README 的格式将从扩展名自动检测
README.md→ GitHub 风格的 Markdown,README.rst→ reStructuredText(不含 Sphinx 扩展)。
你也可以明确指定格式,像这样
[project]
readme = {file = "README.txt", content-type = "text/markdown"}
# or
readme = {file = "README.txt", content-type = "text/x-rst"}
license 和 license-files¶
根据 PEP 639,许可证应通过两个字段声明
license是一个 SPDX 许可证表达式,由一个或多个 许可证标识符 组成。license-files是一个许可证文件 glob 模式列表。
之前的 PEP 曾指定 license 为一个带有 file 或 text 键的表,此格式现已弃用。大多数 构建后端 现在支持下表所示的新格式。
hatchling |
setuptools |
flit-core [2] |
pdm-backend |
poetry-core |
uv-build |
|---|---|---|---|---|---|
1.27.0 |
77.0.3 |
3.12 |
2.4.0 |
2.2.0 |
0.7.19 |
license¶
license 的新格式是一个有效的 SPDX 许可证表达式,由一个或多个 许可证标识符 组成。完整的许可证列表可在 SPDX 许可证列表页面 上找到。支持的列表版本是 3.17 或任何后续兼容版本。
[project]
license = "GPL-3.0-or-later"
# or
license = "MIT AND (Apache-2.0 OR BSD-2-Clause)"
注意
如果你的构建后端尚不支持新格式,导致构建错误提示 license 应为字典/表,请参阅 以上部分 获取更多上下文。现已弃用的格式 在 PEP 621 中有描述。
通常,使用标准、众所周知的许可证是一个好主意,既可以避免混淆,也可以因为某些组织避免使用未经批准的许可证软件。
如果你的项目使用的是没有现有 SPDX 标识符的许可证,你可以创建自定义标识符,格式为 LicenseRef-[idstring]。自定义标识符必须遵循 SPDX 规范,版本 2.2 的 第 10.1 条 或任何后续兼容版本。
[project]
license = "LicenseRef-My-Custom-License"
license-files¶
这是一个许可证文件列表,以及你希望随包一起分发的包含其他法律信息的文件。
[project]
license-files = ["LICEN[CS]E*", "vendored/licenses/*.txt", "AUTHORS.md"]
glob 模式必须遵循规范
字母数字字符、下划线 (
_)、连字符 (-) 和点 (.) 将按字面匹配。支持特殊字符:
*、?、**和字符范围:[]。路径分隔符必须是正斜杠字符 (
/)。模式相对于包含
pyproject.toml的目录,因此不能以斜杠字符开头。不得使用父目录指示符 (
..)。每个 glob 必须至少匹配一个文件。
文字路径是有效的 glob。不被此规范涵盖的任何字符或字符序列都是无效的。
keywords¶
这将帮助 PyPI 的搜索框在人们搜索这些关键字时建议你的项目。
[project]
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
classifiers¶
一个适用于你项目的 PyPI 分类器列表。查看 所有可能的列表。
classifiers = [
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
"Development Status :: 4 - Beta",
# Indicate who your project is intended for
"Intended Audience :: Developers",
"Topic :: Software Development :: Build Tools",
# Specify the Python versions you support here.
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
]
尽管分类器列表常用于声明项目支持的 Python 版本,但此信息仅用于在 PyPI 上搜索和浏览项目,而不用于安装项目。要实际限制项目可以在哪些 Python 版本上安装,请使用 requires-python 参数。
要阻止包上传到 PyPI,请使用特殊的 Private :: Do Not Upload 分类器。PyPI 将始终拒绝带有以 Private :: 开头的分类器的包。
urls¶
与你的项目相关联的 URL 列表,显示在你的 PyPI 项目页面的左侧边栏。
注意
有关 PyPI 和其他打包工具特别知晓的标签列表,请参阅 知名标签,有关 PyPI 特定 URL 处理,请参阅 PyPI 的项目元数据文档。
[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
Issues = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"
请注意,如果标签包含空格,则需要加引号,例如 Website = "https://example.com" 但 "Official Website" = "https://example.com"。
建议用户在适当时为其项目 URL 使用 知名标签,因为元数据使用者(如包索引)可以专门化其展示。
例如,在以下元数据中,MyHomepage 和 "Download Link" 都不是知名标签,因此它们将按字面渲染
[project.urls]
MyHomepage = "https://example.com"
"Download Link" = "https://example.com/abc.tar.gz"
而在以下元数据中,HomePage 和 DOWNLOAD 都具有知名等价物(homepage 和 download),并且可以根据这些语义进行呈现(分别为项目主页和其外部下载位置)。
[project.urls]
HomePage = "https://example.com"
DOWNLOAD = "https://example.com/abc.tar.gz"
高级插件¶
某些软件包可以通过插件进行扩展。例如 Pytest 和 Pygments。要创建此类插件,你需要像这样在 [project.entry-points] 的子表中声明它
[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"
有关更多信息,请参阅 插件指南。
一个完整的例子¶
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "spam-eggs"
version = "2020.0.0"
dependencies = [
"httpx",
"gidgethub[httpx]>4.0.0",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'",
]
requires-python = ">=3.8"
authors = [
{name = "Pradyun Gedam", email = "pradyun@example.com"},
{name = "Tzu-Ping Chung", email = "tzu-ping@example.com"},
{name = "Another person"},
{email = "different.person@example.com"},
]
maintainers = [
{name = "Brett Cannon", email = "brett@example.com"}
]
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
license = "MIT"
license-files = ["LICEN[CS]E.*"]
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python"
]
[project.optional-dependencies]
gui = ["PyQt5"]
cli = [
"rich",
"click",
]
[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
"Bug Tracker" = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"
[project.scripts]
spam-cli = "spam:main_cli"
[project.gui-scripts]
spam-gui = "spam:main_gui"
[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"