pyproject.toml 规范

警告

这是一个技术性的正式规范。有关 pyproject.toml 的简单易懂的用户指南,请参阅 编写你的 pyproject.toml

pyproject.toml 文件作为打包相关工具(以及其他工具)的配置文件。

注意

此规范最初在 PEP 518PEP 621 中定义。

pyproject.toml 文件使用 TOML 编写。目前指定了三个表,即 [build-system][project][tool]。其他表保留供将来使用(工具特定配置应使用 [tool] 表)。

声明构建系统依赖项:[build-system]

[build-system] 表声明了必须安装的任何 Python 级别依赖项,以便成功运行项目的构建系统。

[build-system] 表用于存储与构建相关的数据。最初,该表只有一个有效且强制的键:requires。此键的值必须是一个字符串列表,表示执行构建系统所需的依赖项。此列表中的字符串遵循 版本说明符规范

使用 setuptools 构建的项目的 [build-system] 表示例如下

[build-system]
# Minimum requirements for the build system to execute.
requires = ["setuptools"]

当不存在 pyproject.toml 文件时,构建工具应使用上述示例配置文件作为其默认语义。

工具不应要求 [build-system] 表的存在。pyproject.toml 文件可用于存储除构建相关数据之外的配置详细信息,因此合法地缺少 [build-system] 表。如果文件存在但缺少 [build-system] 表,则应使用上面指定的默认值。如果指定了表但缺少所需字段,则工具应将其视为错误。

为了仅出于说明目的提供 TOML 文件生成数据的类型特定表示,以下 JSON Schema 将匹配数据格式

{
    "$schema": "https://json-schema.fullstack.org.cn/schema#",

    "type": "object",
    "additionalProperties": false,

    "properties": {
        "build-system": {
            "type": "object",
            "additionalProperties": false,

            "properties": {
                "requires": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            },
            "required": ["requires"]
        },

        "tool": {
            "type": "object"
        }
    }
}

声明项目元数据:[project]

[project] 表指定项目的 核心元数据

元数据有两种类型:静态动态。静态元数据直接在 pyproject.toml 文件中指定,不能由工具指定或更改(这包括元数据引用的数据,例如元数据引用的文件内容)。动态元数据通过 dynamic 键(在本规范后面定义)列出,表示工具稍后将提供的元数据。

缺少 [project] 表隐式意味着 构建后端 将动态提供所有键。

唯一需要静态定义的键是

  • 名称

需要但可以静态指定或列为动态的键是

  • 版本

所有其他键都被视为可选,可以静态指定,列为动态,或不指定。

[project] 表中允许的完整键列表是

  • 作者

  • 分类器

  • 依赖项

  • 描述

  • 动态

  • 入口点

  • gui-脚本

  • 导入名称

  • 导入命名空间

  • 关键词

  • 许可证

  • 许可证文件

  • 维护者

  • 名称

  • 可选依赖项

  • 自述文件

  • requires-python

  • 脚本

  • urls

  • 版本

name

项目的名称。

工具应 标准化 此名称,一旦读取即可确保内部一致性。

version

项目的版本,如 版本说明符规范 中定义。

用户应优先指定已标准化的版本。

description

项目的一行摘要描述。如果包含多行,工具可能会报错。

readme

项目的完整描述(即 README)。

此键接受字符串或表。如果它是字符串,则它是相对于 pyproject.toml 的文本文件路径,其中包含完整的描述。工具必须假定文件编码为 UTF-8。如果文件路径以不区分大小写的 .md 后缀结尾,则工具必须假定内容类型为 text/markdown。如果文件路径以不区分大小写的 .rst 结尾,则工具必须假定内容类型为 text/x-rst。如果工具识别的扩展名比此 PEP 多,它们可以在不将此键指定为 dynamic 的情况下推断内容类型。对于未提供内容类型的所有无法识别的后缀,工具必须引发错误。

readme 键也可以接受一个表。此表的 file 键的字符串值表示相对于 pyproject.toml 的文件路径,该文件包含完整的描述。text 键的字符串值是完整的描述。这些键是互斥的,因此如果元数据同时指定这两个键,工具必须引发错误。

readme 键中指定的表还具有一个 content-type 键,该键接受一个字符串,指定完整描述的内容类型。如果元数据未在表中指定此键,工具必须引发错误。如果元数据未指定 charset 参数,则假定为 UTF-8。工具可以选择支持其他编码。工具可以选择支持可以将其他内容类型转换为 核心元数据 支持的内容类型。否则,工具必须对不支持的内容类型引发错误。

requires-python

项目的 Python 版本要求。

license

一个文本字符串,它是有效的 SPDX 许可证表达式,如 许可证表达式 中指定。工具应验证表达式并执行大小写规范化。

此键应在构建后端使用 pyproject.toml 创建的任何和所有分发文件的许可证表达式与指定表达式相同时指定。如果许可证表达式不同,则应将其指定为动态或根本不设置。

遗留规范

此表可以有两个键。 file 键的字符串值是相对于 pyproject.toml 的文件路径,指向包含项目许可证的文件。工具必须假定文件编码为 UTF-8。text 键的字符串值是项目的许可证。这些键是互斥的,因此如果元数据同时指定这两个键,工具必须引发错误。

该表的子键已通过 PEP 639 弃用,转而使用字符串值。

license-files

一个数组,指定项目源树中相对于项目根目录(即包含 pyproject.toml 或遗留项目配置文件(例如 setup.pysetup.cfg 等)的目录)的路径,指向包含将随包分发的许可证和其他法律声明的文件。

字符串必须包含有效的 glob 模式,如 glob 模式 中指定。

模式是相对于包含 pyproject.toml 的目录的。

工具必须假定许可证文件内容是有效的 UTF-8 编码文本,并且应验证此内容,如果不是则引发错误。

构建工具

  • 必须在所有分发档案中包含由列出的模式匹配的所有文件。

  • 必须在核心元数据中的 License-File 字段下列出每个匹配的文件路径。

如果 license-files 键存在并且其值为空数组,则工具不得包含任何许可证文件,也不得引发错误。如果未定义 license-files 键,工具可以决定如何处理许可证文件。例如,它们可以选择不包含任何文件或使用自己的逻辑来发现分发中适当的文件。

authors/maintainers

被视为项目“作者”的人或组织。其确切含义有待解释——它可以列出原始或主要作者、当前维护者或包的所有者。

“maintainers”键与“authors”类似,其确切含义有待解释。

这些键接受一个包含两个键(nameemail)的表数组。这两个值都必须是字符串。name 值必须是有效的电子邮件名称(即在 RFC 822 中,电子邮件之前可以作为名称的任何内容),且不能包含逗号。email 值必须是有效的电子邮件地址。这两个键都是可选的,但表中至少必须指定其中一个键。

使用数据填充 核心元数据 的方法如下

  1. 如果只提供了 name,则该值将酌情填充到 AuthorMaintainer 中。

  2. 如果只提供了 email,则该值将酌情填充到 Author-emailMaintainer-email 中。

  3. 如果同时提供了 emailname,则该值将酌情填充到 Author-emailMaintainer-email 中,格式为 {name} <{email}>

  4. 多个值应以逗号分隔。

keywords

项目的关键词。

classifiers

适用于项目的 Trove 分类器。

弃用 License :: 分类器的使用,工具可以发出警告通知用户。如果同时使用 license 字符串值(转换为 License-Expression 元数据字段)和 License :: 分类器,构建工具可以引发错误。

urls

一个 URL 表,其中键是 URL 标签,值是 URL 本身。有关规范化规则和处理元数据以供展示时的已知规则,请参阅 元数据中的已知项目 URL

入口点

  • TOML 类型:表([project.scripts][project.gui-scripts][project.entry-points]

  • 入口点规范

有三个与入口点相关的表。[project.scripts] 表对应于 入口点规范 中的 console_scripts 组。表的键是入口点的名称,值是对象引用。

[project.gui-scripts] 表对应于 入口点规范 中的 gui_scripts 组。其格式与 [project.scripts] 相同。

[project.entry-points] 表是表的集合。每个子表的名称都是一个入口点组。键和值的语义与 [project.scripts] 相同。用户不得创建嵌套子表,而应将入口点组保持为仅一层深。

如果元数据定义了 [project.entry-points.console_scripts][project.entry-points.gui_scripts] 表,则构建后端必须引发错误,因为它们分别与 [project.scripts][project.gui-scripts] 存在歧义。

dependencies/optional-dependencies

项目的(可选)依赖项。

对于 dependencies,它是一个键,其值是一个字符串数组。每个字符串表示项目的一个依赖项,并且必须格式化为有效的 PEP 508 字符串。每个字符串直接映射到一个 Requires-Dist 条目。

对于 optional-dependencies,它是一个表,其中每个键指定一个额外项,其值是字符串数组。数组的字符串必须是有效的 PEP 508 字符串。键必须是 Provides-Extra 的有效值。数组中的每个值因此成为匹配的 Provides-Extra 元数据的相应 Requires-Dist 条目。

import-names

一个字符串数组,指定项目安装时独占提供的导入名称。每个字符串必须是有效的 Python 标识符或可以为空。导入名称后面可以跟一个分号和术语“private”(例如 "; private"),分号周围可以有任意数量的空白。

项目应列出项目独占提供的所有最短导入名称。如果任何最短名称是点分名称,则从该名称到顶级名称的所有中间名称也应在 import-names 和/或 import-namespaces 中适当列出。例如,一个名为 spam 的单个包项目,具有多个子模块,将只列出 project.import-names = ["spam"]。列出 spam.bacon.eggs 的项目还需要在 import-namesimport-namespaces 中适当考虑 spamspam.bacon。列出所有名称可作为检查导入名称意图是否符合预期的手段。此外,项目应酌情使用 ; private 修饰符列出所有导入名称,无论是公共的还是私有的。

如果一个项目在 import-namesimport-namespaces 中列出相同的名称,则工具必须因歧义而引发错误。

项目可以将 import-names 设置为空数组,以表示一个没有导入名称的项目(即分发文件中没有任何类型的 Python 模块)。

如果用户在 project.dynamic 中声明了该键,则构建后端可以支持动态计算该值。

示例

[project]
name = "pillow"
import-names = ["PIL"]
[project]
name = "myproject"
import-names = ["mypackage", "_private_module ; private"]

import-namespaces

一个字符串数组,指定项目安装时提供但不独占的导入名称。每个字符串必须是有效的 Python 标识符。导入名称后面可以跟一个分号和术语“private”(例如 "; private"),分号周围可以有任意数量的空白。请注意,与 import-names 不同,import-namespaces 不能是空数组。

项目应列出项目独占提供的所有最短导入名称。如果任何最短名称是点分名称,则从该名称到顶级名称的所有中间名称也应在 import-names 和/或 import-namespaces 中适当列出。

此字段用于命名空间包,其中多个项目可以贡献到相同的导入命名空间。在 import-namespaces 中列出相同导入名称的项目可以一起安装而不会相互遮蔽。

如果一个项目在 import-namesimport-namespaces 中列出相同的名称,则工具必须因歧义而引发错误。

如果用户在 project.dynamic 中声明了该键,则构建后端可以支持动态计算该值。

示例

[project]
name = "zope-interface"
import-namespaces = ["zope"]
import-names = ["zope.interface"]

dynamic

指定本 PEP 列出的哪些键是故意未指定的,以便其他工具可以/将动态提供此类元数据。这清楚地划定了哪些元数据是故意未指定且预期保持未指定,而不是稍后通过工具提供的。

  • 构建后端必须遵守静态指定的元数据(这意味着元数据未在 dynamic 中列出该键)。

  • 如果元数据在 dynamic 中指定了 name,则构建后端必须引发错误。

  • 如果 核心元数据 规范将某个字段列为“Required”,则元数据必须静态指定该键或在 dynamic 中列出(否则构建后端必须引发错误,即所需的键不应以任何方式未列在 [project] 表中)。

  • 如果 核心元数据 规范将某个字段列为“Optional”,则如果预期构建后端稍后将为该键提供数据,元数据可以将其列在 dynamic 中。

  • 如果元数据静态指定了一个键,并且该键也列在 dynamic 中,则构建后端必须引发错误。

  • 如果元数据未在 dynamic 中列出某个键,则构建后端无法代表用户填写所需元数据(即,dynamic 是允许工具填写元数据的唯一方式,用户必须选择启用填写)。

  • 如果元数据在 dynamic 中指定了一个键,但构建后端无法确定其数据,则构建后端必须引发错误(如果确定为准确值,则省略数据是可接受的)。

任意工具配置:[tool]

[tool] 表是任何与你的 Python 项目相关的工具(不仅仅是构建工具)指定配置数据的地方,只要它们在 [tool] 中使用子表即可,例如 flit 工具会将其配置存储在 [tool.flit] 中。

需要一种机制来在 tool.* 命名空间内分配名称,以确保不同的项目不会尝试使用相同的子表并发生冲突。我们的规则是,一个项目可以使用子表 tool.$NAME,当且仅当它拥有 Cheeseshop/PyPI 中 $NAME 的条目。

历史

  • 2016 年 5 月:pyproject.toml 文件的初始规范,仅包含一个 [build-system] 和一个 requires 键以及一个 [tool] 表,通过 PEP 518 批准。

  • 2020 年 11 月:[project] 表的规范通过 PEP 621 获得批准。

  • 2024 年 12 月:通过 PEP 639 重新定义了 license 键,添加了 license-files 键,并弃用了 License:: 分类器。

  • 2025 年 9 月:明确 license 键适用于从 pyproject.toml 文件生成的所有分发文件。

  • 2025 年 10 月:通过 PEP 794 添加了 import-namesimport-namespaces 键。