PEP 621 – 在 pyproject.toml 中存储项目元数据
- 作者:
- Brett Cannon <brett at python.org>, Dustin Ingram <di at python.org>, Paul Ganssle <paul at ganssle.io>, Pradyun Gedam <pradyunsg at gmail.com>, Sébastien Eustace <sebastien at eustace.io>, Thomas Kluyver <thomas at kluyver.me.uk>, Tzu-ping Chung <uranusjr at gmail.com>
- 讨论地址:
- Discourse 讨论帖
- 状态:
- 最终版
- 类型:
- 标准追踪
- 主题:
- 打包
- 创建时间:
- 2020-06-22
- 历史记录:
- 2020-06-22, 2020-10-18, 2020-10-24, 2020-10-31
- 决议:
- Discourse 帖子
目录
- 摘要
- 动机
- 基本原理
- 规范
- 向后兼容性
- 安全影响
- 参考实现
- 被拒绝的建议
- 其他表名
- 支持元数据提供者
- 要求规范化的项目名称
- 指定构建时包含的文件
- 将
[project.urls]
表命名为[project.project-urls]
- 拥有单独的
url
/home-page
字段 - 建议工具将开发相关的依赖项放到一个“dev”额外项中
- 使
dynamic
字段仅需要指定缺少的必填字段 readme
字段的不同结构- 允许
readme
字段隐式表示text/plain
dependencies
/optional-dependencies
的其他名称- 删除
maintainers
以与authors
统一 - 支持
project.entry-points
的任意深度表 - 使用结构化的 TOML 字典来指定依赖项
- 要求构建后端在生成 sdist 时更新
pyproject.toml
- 允许工具添加/扩展数据
- 开放问题
- 版权
摘要
此 PEP 指定了如何在项目的 核心元数据 文件中写入一个 pyproject.toml
文件,以便与打包相关的工具能够使用它。
动机
此 PEP 的主要动机是
- 鼓励用户为了速度、易于指定、清晰性和确定性而静态地指定核心元数据,以便构建后端能够轻松地使用它
- 提供一种与工具无关的方式来指定元数据,以便用户能够轻松地学习和在构建后端之间进行切换
- 允许构建后端之间在项目元数据的“无聊部分”进行更多代码共享
具体来说,静态元数据的动机是打包生态系统的一直以来的目标。因此,使其易于静态地指定元数据非常重要。这也意味着提高将数据指定为动态的成本是可以接受的,因为用户应该更倾向于提供静态元数据。
要求区分静态和动态元数据还有助于消除元数据未指定时的歧义。当任何元数据都可能是动态的时,这意味着你永远不知道元数据的缺失是故意的,还是因为它将在以后提供。通过要求指定动态元数据,它消除了元数据未指定时的意图歧义。
此 PEP 不试图标准化构建后端所需的所有可能的元数据,而只是涵盖 核心元数据 规范的元数据,这些元数据在项目之间非常常见,并且可以从静态地和一致地指定中获益。这意味着构建后端仍然可以自由地在像如何指定包含在 wheel 中的文件这样的模式方面进行创新。还包括一个允许用户和构建后端使用的转义门,当他们选择部分放弃此 PEP 时(与完全放弃此 PEP 相比,这同样是可行的)。
此 PEP 也不试图以任何方式改变底层的 核心元数据。此类考虑应在单独的 PEP 中进行,这可能会导致对此 PEP 指定的内容进行更改或添加。
基本原理
此 PEP 的作者遵循的设计指南是
- 在
pyproject.toml
中定义尽可能多的 核心元数据 表示 - 使用转义门静态地定义元数据,以便那些想要在以后通过构建后端动态地定义元数据的人
- 在有意义的地方使用熟悉的名称,但愿意使用更现代的术语
- 尝试在 TOML 文件中保持符合人体工程学的设计,而不是在有意义的情况下反映构建后端在低级别指定元数据的方式
- 向打包生态系统中其他使用 TOML 来存储其元数据的构建后端学习
- 不要试图标准化那些在低级别没有预先存在的标准的事物
- 当使用此 PEP 指定元数据时,它被认为是规范的
规范
在指定项目元数据时,工具 MUST 遵守并尊重此 PEP 中指定的元数据。如果元数据指定不正确,则工具 MUST 抛出错误以通知用户他们的错误。
使用此 PEP 指定的数据被认为是规范的。工具 CANNOT 删除、添加或更改静态指定的 数据。只有在字段被标记为 dynamic
时,工具才能提供“新”值。
细节
表名
工具 MUST 在名为 [project]
的表中指定此 PEP 定义的字段。任何工具都不可添加此 PEP 或后续 PEP 未定义的字段到此表中。对于希望在 pyproject.toml
中存储自身设置的工具,他们可以使用 PEP 518 中定义的 [tool]
表。没有 [project]
表隐含地意味着构建后端将动态提供所有字段。
name
项目的名称。
工具 MUST 要求用户静态地定义此字段。
工具 SHOULD 规范化此名称,如 PEP 503 中所述,以便在内部保持一致性。
version
项目的版本,如 PEP 440 中所支持的。
用户 SHOULD 优先指定已规范化的版本。
description
项目的摘要描述。
readme
项目的完整描述(即README)。
此字段接受字符串或表格。如果为字符串,则为包含完整描述的文本文件的相对路径。工具必须假设文件的编码为UTF-8。如果文件路径以不区分大小写的.md
后缀结尾,则工具必须假设内容类型为text/markdown
。如果文件路径以不区分大小写的.rst
结尾,则工具必须假设内容类型为text/x-rst
。如果工具识别比此PEP更多扩展,则它们可以为用户推断内容类型,而无需将此字段指定为dynamic
。对于未识别后缀且未提供内容类型的情况,工具必须引发错误。
readme
字段也可以采用表格。file
键的字符串值为包含完整描述的文件的相对路径。text
键的字符串值为完整描述。这些键是互斥的,因此如果元数据同时指定了这两个键,则工具必须引发错误。
在readme
字段中指定的表格还具有一个content-type
字段,该字段采用一个字符串,指定完整描述的内容类型。如果元数据未在表格中指定此字段,则工具必须引发错误。如果元数据未指定charset
参数,则假定为UTF-8。工具可以选择支持其他编码。工具可以选择支持它们可以转换为核心元数据支持的内容类型的备选内容类型。否则,工具必须针对不支持的内容类型引发错误。
requires-python
项目的Python版本要求。
license
该表格可能具有两个键之一。file
键的字符串值为包含项目许可证文件的相对文件路径。工具必须假设文件的编码为UTF-8。text
键的字符串值为项目的许可证,其含义与核心元数据中的License
字段的含义相同。这些键是互斥的,因此如果元数据同时指定了这两个键,则工具必须引发错误。
为了允许未来PEP指定对SPDX表达式的支持,license
键的实用字符串值已被有意省略(相同的逻辑适用于任何类型的“类型”字段,指定file
或text
代表的许可证)。
keywords
项目的关键字。
classifiers
Trove 分类器适用于项目。
urls
一个 URL 表格,其中键是 URL 标签,值是 URL 本身。
入口点
有三个与入口点相关的表格。 [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
- 格式: PEP 508 字符串数组 (
dependencies
) 和一个包含 PEP 508 字符串数组的表格 (optional-dependencies
) - 核心元数据:
Requires-Dist
和Provides-Extra
(链接, 链接) - 同义词
项目的(可选)依赖项。
对于 dependencies
,它是一个键,其值为一个字符串数组。每个字符串代表项目的依赖项,**必须**格式化为有效的 PEP 508 字符串。每个字符串直接映射到 核心元数据 中的 Requires-Dist
条目。
对于 optional-dependencies
,它是一个表格,其中每个键指定一个额外的依赖项,其值为一个字符串数组。数组中的字符串必须是有效的 PEP 508 字符串。键**必须**是 核心元数据 中 Provides-Extra
的有效值。因此,数组中的每个值都会成为与匹配的 Provides-Extra
元数据相对应的 Requires-Dist
条目。
dynamic
- 格式:字符串数组
- 核心元数据: N/A
- 没有同义词
指定了此 PEP 列出的哪些字段被故意未指定,以便其他工具可以/将动态提供此类元数据。这明确地区分了哪些元数据是有意未指定且预计将保持未指定,与后来通过工具提供相比。
- 构建后端**必须**遵守静态指定的元数据(这意味着元数据没有在
dynamic
中列出该字段)。 - 如果元数据在
dynamic
中指定了name
,则构建后端**必须**抛出错误。 - 如果 核心元数据 规范将一个字段列为“必需”,那么元数据**必须**静态指定该字段或将其列在
dynamic
中(构建后端**必须**抛出错误,否则,即必需字段不应该以任何方式不在[project]
表格中)。 - 如果 核心元数据 规范将一个字段列为“可选”,则元数据**可以**将其列在
dynamic
中,如果预期构建后端将在稍后提供该字段的数据。 - 如果元数据静态指定了一个字段,并且该字段也在
dynamic
中列出,则构建后端**必须**抛出错误。 - 如果元数据没有在
dynamic
中列出某个字段,那么构建后端**不能**代表用户填写必需的元数据(即dynamic
是允许工具填写元数据的唯一方式,用户必须选择进行填写)。 - 如果元数据在 dynamic 中指定了一个字段,但构建后端无法提供该字段的数据,则构建后端**必须**抛出错误。
示例
[project]
name = "spam"
version = "2020.0.0"
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
requires-python = ">=3.8"
license = {file = "LICENSE.txt"}
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
authors = [
{email = "hi@pradyunsg.me"},
{name = "Tzu-ping Chung"}
]
maintainers = [
{name = "Brett Cannon", email = "brett@python.org"}
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python"
]
dependencies = [
"httpx",
"gidgethub[httpx]>4.0.0",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'"
]
[project.optional-dependencies]
test = [
"pytest < 5.0.0",
"pytest-cov[all]"
]
[project.urls]
homepage = "https://example.com"
documentation = "https://readthedocs.org"
repository = "https://github.com"
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"
向后兼容性
由于这提供了一种新的方式来指定项目 核心元数据,并且使用了新的表格名称,该名称属于 PEP 518 中概述的保留命名空间,因此没有向后兼容性问题。
安全影响
没有直接的安全问题,因为此 PEP 涵盖了如何静态定义项目元数据。任何安全问题都源于工具如何使用元数据以及选择如何对其进行操作。
参考实现
目前还没有任何构建后端实施此 PEP 的概念证明。
被拒绝的建议
其他表名
任何位于 [build-system]
下面的内容
有人担心使用此表格名称会加剧构建元数据和项目元数据之间的混淆,例如通过使用 [build-system.metadata]
作为表格。
[package]
没有获得强有力的支持。
[metadata]
在 [project]
之后最强的竞争者,但最终人们一致同意 [project]
更适合某些子表格,例如 [project.urls]
。
支持元数据提供者
最初有一个提议是在此 PEP 指定的静态元数据和 PEP 517 指定的 prepare_metadata_for_build_wheel()
之间添加一个中间层。这个想法是,如果一个项目想要在构建后端和元数据之间插入自身,将有一个钩子来执行此操作。
最终,作者认为这个想法过于复杂,并且会将 PEP 远离其设计目标,即推动人们尽可能静态地定义核心元数据。
要求规范化的项目名称
虽然这将使工具更容易只使用 PEP 503 中指定的规范化名称,但这个想法最终被拒绝,因为它会损害过渡到使用此 PEP 的项目。
指定构建时包含的文件
作者在设计讨论中相当快地决定,此 PEP 应该专门关注项目元数据,而不是构建元数据。因此,指定哪些文件应该最终出现在源代码发行版或轮文件中的范围不在此 PEP 的范围内。
将 [project.urls]
表命名为 [project.project-urls]
这个建议来自于相应的 核心元数据 为 Project-Url
。但一旦选择了 [project]
的整体表格名称,重复使用“project”一词表明当前较短的名称更合适。
拥有单独的 url
/home-page
字段
虽然 核心元数据 支持它,但为项目的 URL 使用单个字段,同时还支持完整的表格似乎是多余且令人困惑的。
使 dynamic
字段仅需要指定缺少的必填字段
作者考虑过这样的想法,即 dynamic
字段只需要列出缺失的必需字段,而将列出可选字段作为可选。但最终,这违背了尽可能多地静态指定信息的設計目标。
readme
字段的不同结构
readme
字段有一个提议的 readme_content_type
字段,但作者认为字符串/表格混合对于常见情况更实用,同时仍然可以适应更复杂的情况。使用 long_description
和相应的 long_description_content_type
字段也是如此。
表格格式中的 file
键最初提议为 path
,但 file
对应于 setuptools 的 file
键,并且没有强烈的理由选择其中一个而不是另一个。
允许 readme
字段隐式表示 text/plain
作者考虑过允许未指定的內容类型,这些内容类型将默认为 text/plain
,但决定在這種情况下明确说明最好,以防止意外的错误渲染在 PyPI 上,并迫使用户明确他们的意图。
dependencies
/optional-dependencies
的其他名称
作者最初提议 requires
/extra-requires
作为名称,但在对其他打包生态系统的调查表明 Python 是一个例外之后,决定使用当前名称。
在当前名称上进行规范化有助于减少来自其他生态系统的人员在不使用对新手程序员来说可能是陌生的术语的情况下产生的混淆。它还避免了与 requires
在 [build-system]
表中(如 PEP 518 中所述)的潜在混淆。
支持 project.entry-points
的任意深度表
人们担心将 project.entry-points
保持在 1 级子表深度会导致用户混淆,如果他们使用带点的名称并且不习惯使用引号的表名(例如 project.entry-points."spam.magical"
)。但是,支持任意深度 - 例如 project.entry-points.spam.magical
- 将会排除将来任何形式的展开表格式。这也将使构建后端变得复杂,因为它们必须确保遍历整个表结构而不是单个级别,并在值类型上适当地引发错误。
使用结构化的 TOML 字典来指定依赖项
指定项目依赖关系的格式是关于数据格式争论最激烈的话题。它导致了 PEP 631 和 PEP 633 的创建,它们分别代表了本 PEP 中的内容以及更广泛地使用 TOML 字典。关于这些 PEP 的决定可以在 https://discuss.python.org/t/how-to-specify-dependencies-pep-508-strings-or-a-table-in-toml/5243/38 中找到。
作者简要考虑过支持这两种格式,但他们认为这会导致混淆,因为人们需要熟悉两种格式而不是只熟悉一种格式。
要求构建后端在生成 sdist 时更新 pyproject.toml
编写本 PEP 时,sdist 不需要像本 PEP 那样具有静态的规范元数据。然后考虑使用本 PEP 作为将此类元数据放入 sdist 的一种方法。最终,尽管如此,更新 pyproject.toml
的想法并不普遍受到欢迎,因此该想法被拒绝,转而单独追求将 sdist 中的元数据标准化。
允许工具添加/扩展数据
在本 PEP 的早期版本中,允许工具扩展字段的数据。例如,构建后端可以获取版本号并添加一个本地版本,以便在构建轮子时使用。工具还可以添加更多 trove 分类器,例如许可证或支持的 Python 版本。
最终,尽管如此,人们认为最好从严格开始,并根据实际使用情况来考虑放松对数据静止程度的考虑。
开放问题
目前没有。
版权
本文件置于公有领域或根据 CC0-1.0-Universal 许可,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0621.rst
最后修改时间:2023-12-06 16:17:05 GMT