PEP 426 – Python 软件包元数据 2.0
- 作者:
- Alyssa Coghlan <ncoghlan at gmail.com>, Daniel Holth <dholth at gmail.com>, Donald Stufft <donald at stufft.io>
- BDFL 委托:
- Donald Stufft <donald at stufft.io>
- 讨论至:
- Distutils-SIG 邮件列表
- 状态:
- 已撤回
- 类型:
- 信息性
- 主题:
- 打包
- 要求:
- 440, 508, 518
- 创建日期:
- 2012 年 8 月 30 日
- 发布历史:
- 2012 年 11 月 14 日, 2013 年 2 月 5 日, 2013 年 2 月 7 日, 2013 年 2 月 9 日, 2013 年 5 月 27 日, 2013 年 6 月 20 日, 2013 年 6 月 23 日, 2013 年 7 月 14 日, 2013 年 12 月 21 日
- 取代:
- 345
- 取代者:
- 566
摘要
本 PEP 描述了一种用于在 Python 生态系统中进行软件分发相关元数据发布和交换的机制。它包括字段名称的详细信息、其语义和用法。
本文档规定了从未发布的元数据格式 2.0 版本。
版本 1.0 在 PEP 241 中进行了规定。版本 1.1 在 PEP 314 中进行了规定。版本 1.2 在 PEP 345 中进行了规定。
元数据格式 2.0 版本提议从直接定义自定义键值文件格式迁移,改为定义一个 JSON 兼容的内存表示,该表示可用于在其他上下文中定义元数据表示(例如 API 和存档格式定义)。
此版本还定义了一个正式的扩展机制,允许添加新字段以满足特定目的,而无需更新核心元数据格式。
关于 PEP 历史的说明
此 PEP 最初被推迟了很长一段时间,从 2013 年 12 月到 2017 年 3 月,因为 distutils-sig 忙于处理其他一系列更改。这些更改包括
- 在 PEP 425 中定义二进制兼容性标记格式
- 在 PEP 427 中定义二进制存档格式(
wheel) - 在 PEP 440 中明确定义版本控制和版本比较
- 在 PEP 503 中明确定义 PyPI “simple”API
- 在 PEP 508 中明确定义依赖项说明符和额外项系统
- 在 PEP 518 中声明静态构建系统依赖项(
pyproject.toml) - 将 PyPI 托管迁移到 Rackspace,并将其置于 Fastly CDN 后面
- 在 PEP 453 中将
pip默认随 CPython 发布,并在 PEP 477 中将其移植到 Python 2.7 - 将 packaging.python.org 确立为 Python 打包生态系统文档的通用访问点
- 将 packaging.python.org 的“specifications”部分迁移为跟踪打包相关 PEP 的中心位置
投入时间处理这些更改,为哪些元数据格式更改真正可取,以及哪些可以从修订后的规范中省略(仅仅是为了“改变而改变”)提供了额外的视角。
它还允许将许多对核心软件发布和分发活动不关键的功能移出到 PEP 459,这是一个关于一系列标准元数据扩展的独立提案,该提案提供了关于发布的其他可选信息。
截至 2017 年 9 月,它再次被推迟,理由是它实际上并没有帮助解决任何特别紧迫的问题
- JSON 表示将通过定义现有元数据 1.2 字段的转换来更好地处理
- 过去几年定义的新字段的澄清以及对规范管理过程的相关更改将更好地在 小规范版本更新 中介绍
最后,该 PEP 于 2018 年 2 月被撤销,取而代之的是 PEP 566(该 PEP 遵循更渐进的策略)。
目的
本 PEP 的目的是为软件发布工具和软件集成工具在 Python 生态系统中的通信定义一个通用的元数据交换格式。一个关键目标是支持在该生态系统中进行完全的依赖分析,而无需执行执行分析的任意 Python 代码。另一个目标是默认鼓励良好的软件分发实践,同时继续支持目前几乎所有 Python 包索引(发布者和集成者)的现有实践。最后,目标是支持一个透明的、对最终用户无感知的当前使用的元数据格式的升级路径。
该设计借鉴了 Python 社区近 20 年的 distutils 基于软件分发经验,并融合了其他分发系统的想法和概念,包括 Python 的 setuptools、pip 和其他项目,Ruby 的 gems,Perl 的 CPAN,Node.js 的 npm,PHP 的 composer 以及 Linux 打包系统,如 RPM 和 APT。
虽然此格式的细节针对 Python 生态系统,但其中一些想法也可能在其他依赖管理生态系统的未来演进中有用。
Python 软件的开发、分发和部署
本 PEP 中的元数据设计基于对软件开发和分发过程的特定概念模型。该模型包含以下阶段
- 软件开发:此阶段涉及处理特定应用程序的源代码签出,以添加功能和修复错误。预计在此阶段的开发者将需要能够构建软件、运行软件的自动化测试套件、运行项目特定的实用脚本并发布软件。
- 软件发布:此阶段涉及获取已开发的软件并使其可供软件集成者使用。这包括创建本 PEP 中定义的描述性元数据,以及使软件可用(通常通过将其上传到索引服务器)。
- 软件集成:此阶段涉及获取已发布的软件组件并将其组合成一个连贯的集成系统。这可以通过直接使用 Python 特定跨平台工具来完成,或者可以通过转换为与开发语言无关的平台特定打包系统来完成。
- 软件部署:此阶段涉及获取已集成的软件组件并将其部署到软件将实际执行的目标系统上。
发布和集成阶段统称为分发阶段,在此阶段分发的单个软件组件正式称为“分发包”,但更通俗地称为“包”(依赖上下文来区分它们与“带有子模块的模块”之类的 Python 包)。
这些阶段的确切细节会因具体用例而异。将 Web 应用程序部署到公共平台即服务提供商、发布 Web 框架或科学库的新版本、创建集成的 Linux 发行版或升级安全 enclave 中运行的自定义应用程序,都是此元数据设计应能处理的情况。
因此,本 PEP 中描述的元数据的复杂性直接源于各种场景下与软件开发、分发和部署相关的实际复杂性。
支持性定义
本文件中的关键词“必须”、“不得”、“需要”、“应”、“不应”、“建议”、“不建议”、“可以”和“可选”应根据RFC 2119的描述进行解释。
“项目”是指可供集成的软件组件。项目包括 Python 库、框架、脚本、插件、应用程序、数据集合或其他资源,以及它们的各种组合。公开的 Python 项目通常在 Python 包索引 上注册。
“发布”是唯一标识的项目快照。
“分发包”是用于发布和分发发布的打包文件。
根据上下文,“包”可能指分发,也可能指具有 __path__ 属性因此也可能具有可导入子模块的可导入 Python 模块。
“源代码存档”和“VCS 签出”均指发布前的原始源代码,在创建 sdist 或二进制存档之前。
“sdist”是一种发布格式,提供分发元数据和创建分发二进制存档所必需的任何源文件。从 sdist 创建二进制存档需要系统上提供相应的构建工具。
“二进制存档”只需要将预先构建的文件移动到目标系统上的正确位置即可。由于 Python 是一种动态绑定的跨平台语言,许多所谓的“二进制”存档将只包含纯 Python 源代码。
“贡献者”是共同开发软件组件的个人和组织。
“发布者”是使软件组件可供集成(通常通过将分发上传到索引服务器)的个人和组织。
“集成者”是将已发布的分发作为应用程序或更大系统的组件合并的个人和组织。
“构建工具”是指旨在在开发系统上运行的自动化工具,用于生成源和二进制分发存档。构建工具也可能被集成工具调用,以便从 sdist 而非预先构建的二进制存档中构建软件。
“索引服务器”是指发布版本和依赖项元数据并对允许的元数据设置约束的活动分发注册表。
“公共索引服务器”是指允许未经身份验证的第三方上传分发的索引服务器。 Python 包索引 是一个公共索引服务器。
“发布工具”是指旨在在开发系统上运行的自动化工具,并将源和二进制分发存档上传到索引服务器。
“集成工具”是指消耗索引服务器或其他指定源发布的元数据和分发存档,并以某种方式使用它们的自动化工具,例如安装它们或将其转换为平台特定的打包格式。
“安装工具”是指专门设计在部署目标上运行的集成工具,它们从索引服务器或其他指定位置消耗源和二进制分发存档,并将它们部署到目标系统。
“自动化工具”是一个总称,涵盖构建工具、索引服务器、发布工具、集成工具以及任何生成或消耗分发版本和依赖项元数据的软件。
“旧元数据”是指此元数据规范的早期版本,以及 setuptools 项目定义的辅助元数据文件格式。
“Distro”被用作 Linux 发行版的首选术语,以避免与 Python 特定的“分发包”术语混淆。
“限定名称”是带点的 Python 标识符。对于导入的模块和包,限定名称可通过 __name__ 属性获得,而对于函数和类,则可通过 __qualname__ 属性获得。
“完全限定名称”唯一地定位 Python 模块命名空间中的一个对象。对于导入的模块和包,它与限定名称相同。对于其他 Python 对象,完全限定名称由包含的模块或包的限定名称、冒号(:)以及对象相对于包含的模块或包的限定名称组成。
“前缀名称”以限定名称开头,但不一定是限定名称 - 它可能包含其他点分隔的段,这些段不是有效的标识符。
分发的集成与部署
分发元数据的主要目的是支持分发作为更大应用程序和系统一部分的集成和部署。
集成和部署反过来可以细分为更小的步骤。
- 构建:构建步骤是将 VCS 签出、源存档或 sdist 转换为二进制存档的过程。必须提供依赖项才能构建并创建分发二进制存档(包括安装在目标系统上的任何文档)。
- 安装:安装步骤包括将分发及其所有运行时依赖项放到目标系统上。在此步骤中,分发可能已经存在于系统上(在升级或重新安装时),或者它可能是一个全新的安装。
- 运行时:这是分发安装在目标系统上之后的正常使用。
这三个步骤都可以在目标系统上直接发生。或者,可以通过使用分发发布者提供的二进制存档,或在部署之前在单独的系统上创建二进制存档来分离构建步骤。后一种方法的优点是它最小化了需要在部署目标上安装的依赖项(因为构建依赖项只需要在构建系统上)。
分发包的已发布元数据应允许集成者,在构建和集成工具的帮助下,能够
- 获取用于创建分发的原始源代码
- 识别并检索使用分发所需的依赖项(如果存在)
- 识别并检索从源代码构建分发所需的依赖项(如果存在)
- 识别并检索运行分发测试套件所需的依赖项(如果存在)
分发的开发与发布
分发元数据的次要目的是支持开发阶段中软件贡献者和发布者之间的有效协作。
分发的已发布元数据应允许贡献者和发布者,在构建和发布工具的帮助下,能够
- 执行有效集成和部署分发所需的所有相同活动
- 识别并检索开发和发布分发所需的其他依赖项
- 指定使用分发所需的依赖项(如果存在)
- 指定从源代码构建分发所需的依赖项(如果存在)
- 指定运行分发测试套件所需的依赖项(如果存在)
- 指定开发和发布分发所需的其他依赖项(如果存在)
元数据格式
本 PEP 中定义的格式是 Python 分发元数据的内存表示,表示为字符串键的字典。各个条目的允许值为字符串、字符串列表和其他嵌套的字符串键字典。
除非另有说明,分发元数据中的字典键必须是有效的 Python 标识符,以支持基于属性的元数据访问 API。
各个字段的描述显示了键名和值作为 JSON 映射的一部分进行序列化的示例。
除非另有说明,被标识为核心元数据的字段是必需的。自动化工具不得接受缺少核心元数据的分发作为有效的 Python 分发。
所有其他字段都是可选的。如果分发未提供这些字段,自动化工具也必须能够正常运行,除非那些操作专门需要被省略的字段。
自动化工具不得为缺失的字段插入dummy 数据。如果必需字段未提供有效值,则元数据和关联的分发必须被拒绝为无效。如果可选字段未提供有效值,则该字段必须完全省略。自动化工具可以从其他信息源(如版本控制系统)自动派生有效值。
自动化工具,特别是公共索引服务器,可以对元数据施加除了本 PEP 中列出的以外的其他长度限制。如果必要以保护服务的完整性,应基于可用资源和服务提供商对合理元数据容量要求的判断来施加此类限制。
元数据文件
本 PEP 中定义的信息被序列化为 pysdist.json 文件,以用于某些用例。这些文件包含 UTF-8 编码的 JSON 元数据。
每个元数据文件由一个单独的序列化映射组成,字段如本 PEP 中所述。在序列化元数据时,自动化工具应按字母顺序对所有键和列表元素进行排序,以简化任何更改的审查。
预计这些元数据文件将有三个标准位置
- 作为
sdist源分发存档中的{distribution}-{version}.dist-info/pysdist.json文件 - 作为
wheel二进制分发存档中的{distribution}-{version}.dist-info/pysdist.json文件 - 作为本地 Python 安装数据库中的
{distribution}-{version}.dist-info/pysdist.json文件
此文件预计在这三个位置都相同 - 它在从源树创建源存档或二进制存档时生成,然后在安装时或从源存档构建二进制存档时保持不变。
注意
这些位置有待确认,因为它们取决于 sdist 2.0 的定义和修订后的安装数据库标准。在此 PEP 获得批准后,还将有一个 wheel 1.1 格式更新,强制提供 2.0+ 元数据。
请注意,即使包含位置的版本太低而无法指示它们是有效的,这些元数据文件也可能被处理。特别是,未版本化的 sdist 存档、未版本化的安装数据库目录和 wheel 规范的 1.0 版本可能仍然提供 pysdist.json 文件。
注意
在本次规范被正式标记为 Active 之前,建议遵循草案格式的工具使用备用文件名,如 metadata.json 或 pep426-20131213.json,以避免与最终标准化的文件冲突。
其他参与 Python 分发的工具也可以使用此格式。
请注意,这些元数据文件是由构建工具基于其他输入格式(如 setup.py 和 pyproject.toml)生成的,而不是直接用作数据输入格式。在发布过程中生成元数据也有助于处理特定版本字段(包括源代码 URL 和版本字段本身)。
为了向后兼容旧的安装工具,元数据 2.0 文件可以与旧元数据一起分发。
索引服务器可以允许上传仅包含旧元数据的分发,安装工具也可以允许安装仅包含旧元数据的分发。
自动化工具可以尝试将旧元数据自动转换为本 PEP 中描述的格式。有关有效转换的建议在附录 A 中给出。
元数据验证
一个 jsonschema 描述的分发元数据 可用。
此模式目前不处理某些更复杂的字符串字段的验证(而是将其视为不透明字符串)。
除非另有说明,元数据中的所有 URL 字段必须符合 RFC 3986。
注意
当前版本的模式文件涵盖了 PEP 的前一个草案,尚未更新以区分核心依赖解析元数据和多个标准扩展,也未更新以适应当前草案与早期草案之间的各种其他差异。
核心元数据
本节规定了每个 Python 分发必需的核心元数据字段。
发布工具必须确保在发布分发时至少包含这些字段。
索引服务器在分发上传时必须确保元数据中至少包含这些字段。
安装工具默认必须拒绝安装缺少一个或多个这些字段的分发,但可以允许用户强制进行安装。
元数据版本
文件格式的版本;"2.0" 是唯一合法的取值。
消费元数据的自动化工具在 metadata_version 大于其支持的最高版本时应发出警告,在 metadata_version 的主版本号大于其支持的最高版本时必须失败(如 PEP 440 中所述,主版本号是第一个点之前的值)。
为了更广泛的兼容性,构建工具可以选择使用包含所有必需字段的最低元数据版本来生成分发元数据。
示例
"metadata_version": "2.0"
生成器
生成文件的程序名称(及可选版本),如果有的话。手动创建的文件将省略此字段。
示例
"generator": "flit"
"generator": "setuptools (34.3.1)"
名称
分发的名称,如 PEP 508 中定义的。
由于分发名称用作 URL、文件名、命令行参数的一部分,并且还必须与其他打包系统互操作,因此允许的字符受限于
- ASCII 字母(
[a-zA-Z]) - ASCII 数字(
[0-9]) - 下划线(
_) - 连字符(
-) - 点(
.)
分发名称必须以 ASCII 字母或数字开头和结尾。
自动化工具必须拒绝不符合要求的名称。一个用于强制执行这些约束的正则表达式(在以 re.IGNORECASE 运行)是
^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$
所有分发名称的比较都必须不区分大小写,并且必须将连字符和下划线视为等效。
索引服务器可以考虑将 Unicode Consortium 在 TR39: Unicode Security Mechanisms 中定义的“混淆字符”(confusable characters)视为等效。
允许未经身份验证的第三方注册任意分发名称的索引服务器在注册新分发时(因此将它们作为重复项拒绝)应考虑混淆字符的等效性。
集成工具不得默默接受混淆的替代拼写作为匹配请求的分发名称。
截至撰写本文时,Unicode Consortium 在 TR39 中指定的 ASCII 子集中的混淆字符是
1(数字一)、l(拉丁小写字母 l)和I(拉丁大写字母 I)0(数字零)和O(拉丁大写字母 O)
示例
"name": "ComfyChair"
版本
分发的公共或本地版本标识符,如 PEP 440 中定义的。版本标识符设计用于自动化工具消费,并支持各种灵活的版本规范机制(有关详细信息,请参阅 PEP 440)。
版本标识符必须符合 PEP 440 中定义 的格式。
版本标识符在每个项目中必须是唯一的。
索引服务器可能对本地版本标识符的使用施加限制,如 PEP 440 中所述。
示例
"version": "1.0a2"
总结
分发功能的简短摘要。
此字段的长度应少于 512 个字符,必须少于 2048 个。
此字段不应包含任何换行符。
应将更完整的描述包含在分发的 sdist 的单独文件中。有关更多信息,请参阅 PEP 459 中的 python-details 扩展。
示例
"summary": "A module that is more fiendish than soft cushions."
源代码元数据
本节规定了提供用于生成此分发的源代码的标识详细信息的字段。
所有这些字段都是可选的。如果分发未提供这些字段,自动化工具也必须能够正常运行,包括在请求依赖于其中一个字段的操作时干净地失败。
源代码标签
源代码标签是文本字符串,具有最少的定义语义。它们旨在允许原始源代码被明确识别,即使集成者已对特定分发应用了额外的本地修改。
为了确保源代码标签可以方便地作为文件名和 URL 的一部分,并避免十六进制哈希表示中的格式不一致,它们必须限制在以下允许的字符集中
- 小写 ASCII 字母(
[a-z]) - ASCII 数字(
[0-9]) - 下划线(
_) - 连字符(
-) - 点(
.) - 加号(
+)
源代码标签必须以 ASCII 字母或数字开头和结尾。
一个用于强制执行这些约束的正则表达式(在以 re.IGNORECASE 运行)是
^([A-Z0-9]|[A-Z0-9][A-Z0-9._-+]*[A-Z0-9])$
项目的源代码标签不得匹配该项目的任何已定义版本。此限制确保版本标识符和源代码标签之间没有歧义。
示例
"source_label": "1.0.0-alpha.1"
"source_label": "1.3.7+build.11.e0f985a"
"source_label": "v1.8.1.301.ga0df26f"
"source_label": "2013.02.17.dev123"
源代码 URL
包含一个完整 URL 的字符串,其中可以下载此分发特定版本的源代码。
源代码 URL 在每个项目中必须是唯一的。这意味着 URL 不能是 "https://github.com/pypa/pip/archive/main.zip" 这样的,而是必须是 "https://github.com/pypa/pip/archive/1.3.1.zip"。
源代码 URL 必须引用源存档或在线版本控制系统中的标签或特定提交,该系统允许创建合适的 VCS 签出。它主要面向希望从原始源形式重新创建分发的集成者。
所有源代码 URL 引用都应指定安全传输机制(如 https)并包含 URL 中的预期哈希值以供验证。如果源代码 URL 未包含任何哈希信息,或包含工具不理解的哈希信息,或包含工具认为太弱而不值得信赖的选定哈希算法,自动化工具应至少发出警告,并可能拒绝依赖该 URL。如果此类源代码 URL 还使用不安全传输,自动化工具不应依赖该 URL。
对于源存档引用,可以通过将 `
截至 2017 年,建议对源代码 URL 使用 'sha256' 哈希,因为该哈希尚未被发现易受恶意碰撞生成攻击,同时又在客户端系统中广泛可用。
对于版本控制引用,应使用 `VCS+protocol` 方案来标识版本控制系统和安全传输,并应使用支持基于哈希的提交标识符的版本控制系统。自动化工具可以省略有关版本控制系统中缺少哈希的警告,如果该版本控制系统不提供基于哈希的提交标识符。
为了处理不支持将提交或标签引用直接包含在 URL 中的版本控制系统,这些信息可以通过使用 `@
注意
这与 pip 支持的现有 VCS 引用表示法并不完全相同。首先,分发名称是独立字段,而不是嵌入在 URL 中。其次,即使基于标签检索,也包含提交哈希,以满足上述要求,即每个链接都应包含一个哈希,以使内容更难伪造(创建一个包含特定标签的恶意仓库很容易,创建一个包含特定*哈希*的仓库则不然)。
示例
"source_url": "https://github.com/pypa/pip/archive/1.3.1.zip#sha256=2dc6b5a470a1bde68946f263f1af1515a2574a150a30d6ce02c6ff742fcc0db8
"source_url": "git+https://github.com/pypa/pip.git@1.3.1#7921be1537eac1e97bc40179a57f0349c2aee67d"
"source_url": "git+https://github.com/pypa/pip.git@7921be1537eac1e97bc40179a57f0349c2aee67d"
语义依赖
依赖项元数据允许已发布的项目利用其他已发布项目提供的功能,而无需捆绑这些项目的特定发布副本。
语义依赖项允许发布者不仅指明需要哪些其他项目,还指明*为什么*需要它们。这些额外信息允许集成者仅安装特定活动所需的依赖项,从而在受限环境中更容易最小化安装足迹(无论这些限制的原因是什么)。
默认情况下,依赖项声明被假定为“运行时依赖项”:即使用已发布版本实际需要的其他发布。
此外,发布还可以声明四种不同类型的可选依赖项
test依赖项:运行此版本测试套件所需的其他发布,但仅使用它本身是不需要的(例如nose2或pytest)build依赖项:从源代码构建此可部署二进制版本所需的其他发布(例如flit或setuptools)doc依赖项:构建此分发文档所需的其他发布(例如sphinx构建工具)dev依赖项:在处理此分发时所需的其他发布,但不能恰好归入其他可选依赖项类别(例如pylint、flake8)。dev依赖项也被视为test、build和doc依赖项的组合,而无需列出三次。
这些可选类别称为额外项。除了四个标准类别外,项目还可以通过额外项字段声明自己的自定义类别。
此外,还有两个标准额外类别,它们暗示对其他额外项的依赖
alldev:表示test、build、doc、dev额外项all:如果没有其他定义,则表示所有已声明的额外项
依赖项管理在很大程度上依赖于 PEP 440 中定义的版本标识和规范方案,以及 PEP 508 中定义的依赖项规范、额外项和环境标记方案。
所有这些字段都是可选的。如果分发未提供这些字段,自动化工具必须正常运行,假设缺失的字段表示“此分发不适用”。
将依赖映射到开发和分发活动
依赖项的各种类别基于上述各种分发和开发活动,并规定了为指定活动应安装的依赖项
- 必需的运行时依赖项
- 无条件依赖项
- 必需的构建依赖项
- the
buildextra - the
devextra - 如果在构建过程中运行分发测试套件,还应安装无条件依赖项和
testextra
- the
- 必需的开发和发布依赖项
- 无条件依赖项
- the
testextra - the
buildextra - the
docextra - the
devextra
应使用 Extras(可选依赖项) 中描述的符号来确定各种操作的确切安装内容。
安装工具应在无法满足依赖项时报告错误,至少应发出警告,并且可以允许用户强制继续安装。
有关将这些依赖项映射到 RPM SPEC 文件的概述,请参阅附录 B。
额外项
一组可选的依赖项集,可用于在依赖字段中定义条件依赖项。有关详细信息,请参阅 Extras(可选依赖项)。
额外项的名称必须遵守与分发名称相同的限制。
以下额外项名称默认可用,并且不得在此字段中显式声明
allalldevbuilddevdoctest
示例
"extras": ["warmup", "tea"]
依赖
实际运行此发布所需的发布要求列表。
公共索引服务器可以禁止在此字段中使用严格的版本匹配子句或直接引用。
示例
"dependencies":
{
"requires": ["SciPy", "PasteDeploy", "zope.interface > 3.5.0"]
},
{
"requires": ["pywin32 > 1.0"],
"environment": "sys_platform == 'win32'"
},
{
"requires": ["SoftCushions"],
"extra": "warmup"
}
]
虽然许多依赖项对于使用项目发布本身是必需的,但其他依赖项仅在特定平台或仅当需要发布器的特定可选功能时才需要。
为了处理这个问题,发布依赖项说明符是具有以下子字段的映射
requires:满足依赖项所需的要求列表extra:请求并一起安装的可选依赖项集名称。有关详细信息,请参阅 Extras(可选依赖项)。environment:定义需要这些依赖项的环境的环境标记。环境标记的语法和功能在 PEP 508 中定义。
在 requires 列表中的各个条目是使用 PEP 508 中定义的依赖项声明格式的字符串,但有一个例外:环境标记不得包含在单个依赖项声明中,而是包含在单独的 environment 字段中。
requires 是唯一必需的子字段。当它是唯一的子字段时,依赖项称为*无条件*。如果指定了 extra 或 environment,则依赖项是*有条件*的。
所有三个字段都可以提供,这表明只有在特定环境中请求了命名额外项时,才需要这些依赖项。
自动化工具必须将相关的依赖项说明符(具有公共 extra 和 environment 值的说明符)组合成一个单独的说明符,在序列化元数据时列出多个要求。
尽管存在此必需的规范化,但相同的额外项名称或环境标记可以出现在多个条件依赖项中。例如,当一个额外项本身在特定环境中仅需要其部分依赖项时,可能会发生这种情况。只有额外项和环境标记的组合才要求在依赖项说明符列表中是唯一的。
除了六个标准额外类别外,从依赖项说明符引用的任何额外项都必须在此分发的 Extras 字段中命名。这有助于避免拼写错误,并且无需扫描完整的依赖项集即可轻松识别可用额外项。
为了将额外项定义作为另一个额外项的一部分重用,项目发布可以声明对自身的依赖。为了避免这些情况下的无限递归,自动化工具必须对项目自身的反向依赖项进行特殊处理。
元数据扩展
元数据的扩展可能存在于 extensions 键下的映射中。键必须是有效的前缀名称,而值本身必须是嵌套映射。
两个键名是保留的,除非如下所述,否则扩展不得使用
extension_versioninstaller_must_handle
以下示例显示了 PEP 459 中的 python.details 和 python.commands 标准扩展
"extensions" : {
"python.details": {
"license": "GPL version 3, excluding DRM provisions",
"keywords": [
"comfy", "chair", "cushions", "too silly", "monty python"
],
"classifiers": [
"Development Status :: 4 - Beta",
"Environment :: Console (Text Based)",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)"
],
"document_names": {
"description": "README.rst",
"license": "LICENSE.rst",
"changelog": "NEWS"
}
},
"python.commands": {
"wrap_console": [{"chair": "chair:run_cli"}],
"wrap_gui": [{"chair-gui": "chair:run_gui"}],
"prebuilt": ["reduniforms"]
},
}
扩展名由分发定义,这些分发然后以某种方式利用其他已发布的元数据。
为了减少名称冲突的可能性,扩展名应使用与定义扩展含义的分发模块相对应的前缀。这种做法也将使查找元数据扩展的权威文档更加容易。
元数据扩展允许开发工具在元数据中记录可能在分发后期阶段有用的信息,但对于依赖解析或构建软件并非必需。
扩展版本控制
扩展必须使用 extension_version 键进行版本控制。但是,如果省略此键,则隐含版本为 1.0。
消费扩展元数据的自动化工具在 extension_version 大于其支持的最高版本时应发出警告,在 extension_version 的主版本号大于其支持的最高版本时必须失败(如 PEP 440 中所述,主版本号是第一个点之前的值)。
为了更广泛的兼容性,构建工具可以选择使用包含所有必需字段的最低元数据版本来生成扩展元数据。
必需的扩展处理
项目可能认为正确处理某些扩展对于正确安装软件至关重要。这是通过将 installer_must_handle 字段设置为 true 来表示的。将其设置为 false 或完全省略表示开发者不认为在安装分发时处理扩展是强制性的。
如果 installer_must_handle 设置为 true 且安装工具无法处理该特定扩展(无论是直接还是通过工具特定的插件系统),安装工具必须失败。
如果安装工具在尝试从 wheel 存档安装时遇到其不理解的必需扩展,它可能会回退到尝试从源代码安装,而不是完全失败。
额外项(可选依赖)
如 PEP 508 中定义,额外项是允许项目发布的某个可选方面功能的其他依赖项,通常对应于代码中的 `try: import optional_dependency ... 块。它们也用于指示除正常运行时使用之外的其他活动的语义依赖项(如测试、构建或处理组件)。
为了支持在有或没有可选依赖项的情况下使用发布,它们被单独列出,与发布的运行时依赖项分开,并且必须显式请求,无论是通过另一个项目的依赖项说明,还是在向安装工具发出命令时。
具有可选依赖项的分发示例
"name": "ComfyChair",
"extras": ["warmup"]
"dependencies": [
{
"requires": ["SoftCushions"],
"extra": "warmup"
},
{
"requires": ["cython"],
"extra": "build"
}
]
其他分发通过在指定依赖项时,将相关的额外项名称放在分发名称之后,并用方括号括起来,来要求其他依赖项。一个依赖项中的多个额外项可以通过放置来请求。
如果标准的 all 额外项没有显式声明条目,则集成工具应隐含地将其定义为对项目显式声明的所有额外项的依赖。
如果标准的 alldev 额外项没有显式声明条目,则集成工具应隐含地将其定义为对标准 test、build、doc 和 dev 额外项的依赖。
然后,完整的依赖项要求集基于无条件依赖项以及任何请求的额外项的依赖项。
依赖项示例(仅显示 requires 子字段)
"requires": ["ComfyChair"]
-> requires ``ComfyChair`` only
"requires": ["ComfyChair[warmup]"]
-> requires ``ComfyChair`` and ``SoftCushions``
"requires": ["ComfyChair[all]"]
-> requires ``ComfyChair`` and ``SoftCushions``, but will also
pick up any new extras defined in later versions
更新元数据规范
元数据规范可以通过澄清来更新,而无需发布新的 PEP 或更改元数据版本。
更改现有字段的含义或添加新功能(除了通过扩展机制外)需要一个在新 PEP 中定义的新元数据版本。
附录 A:旧元数据转换说明
从旧元数据转换为元数据 2.0 的参考实现是
- the wheel project,它将
bdist_wheel命令添加到setuptools - the Warehouse project,它最终将被迁移到 Python Packaging Authority,作为下一代 Python 包索引实现
- the distlib project,它源自为
distutils2项目创建的核心打包基础设施
注意
这些工具尚未针对切换到标准扩展以处理多个字段进行更新。
虽然预计可能会有一些边缘情况需要手动干预才能干净地转换,但该规范已设计成允许几乎所有 PyPI 上的项目进行完全自动化的转换。
元数据转换(尤其是在索引服务器方面)是允许安装和分析工具开始从新元数据格式中受益所必需的步骤,而无需等待开发者升级到更新的构建系统。
附录 B:将依赖声明映射到 RPM SPEC 文件
作为将此 PEP 映射到 Linux 发行版包的示例,假设一个没有定义任何额外项的示例项目在 SPEC 文件中被拆分为 2 个 RPM:example 和 example-devel。
无条件依赖项将映射到“example”RPM 的 Requires 依赖项(将与 Linux 相关的环境标记映射到 SPEC 文件条件也将允许正确处理它们)。
the build 和 dev 额外依赖项将映射到“example”RPM 的 BuildRequires 依赖项。根据 RPM 的 `%check` 部分的定义方式,test 额外项也可能被映射到 RPM 的 BuildRequires 声明。
在 dev、test、build 和 doc 额外项中定义的所有与 Linux 相关的依赖项将成为“example-devel”RPM 的 Requires 依赖项。
像 Sphinx 这样的工具链依赖项将包含在 build 额外项中(例如,如果 man pages 包含在构建的分发中),或者包含在 doc 额外项中(例如,如果文档仅通过 Read the Docs 或项目网站发布)。这将足以允许自动化转换器将其映射到 spec 文件中的相应依赖项。
如果项目定义了任何额外项,则可以将它们映射到其他虚拟 RPM,并根据依赖项说明的详细信息添加适当的 BuildRequires 和 Requires 条目。或者,它们可以映射到其他系统包管理器功能(如弱依赖项)。
元数据扩展格式还应提供一种方式,将特定于发行版的提示包含在 upstream 项目元数据中,而无需手动在特定于发行版的格式中重复 upstream 元数据。
附录 C:与 PEP 345 的差异摘要
- 元数据版本现为 2.0,并为处理版本更改指定了语义。
- 日益复杂的临时“键:值”格式已被更结构化的 JSON 兼容格式取代,该格式易于表示为 Python 字典、字符串、列表。
- 大多数字段现在都是可选的,并且明确禁止填充缺失字段的 dummy 数据。
- 明确允许在不发布新版本规范的情况下进行实时澄清。
- 该 PEP 现在试图提供更多关于字段存在的原因以及它们预期用途的解释,而不是仅仅描述允许的内容。
- 将版本方案更改为基于 PEP 440 而不是 PEP 386。
- 添加了源代码标签机制,如 PEP 440 中所述。
- 在 PEP 508 中正式定义了依赖项声明、额外项和环境标记。
- 通过额外的保留额外项名称支持不同类型的依赖项。
- 更新了废弃机制。
- 一个定义明确的元数据扩展机制,并将任何非依赖解析必需的字段迁移到标准扩展。
- 与 Charles Schulz 和 Peanuts 的所有尊重一样,许多示例已更新,以更符合 Python 的主题;)
重大更改的理由在以下各节中给出。
元数据版本语义
规范的主版本号和次版本号增量现在已指定,并遵循与 wheel 格式在 PEP 427 中指定的格式版本语义相同的模型:次版本号增量在被只理解相同主版本号的早期元数据版本的工具处理时,应该表现得合理,而主版本号增量可能包含与现有工具不兼容的更改。
规范的主版本号已相应增加,因为解释 PEP 426 元数据显然不能根据早期元数据规范进行解释。
每当规范的主版本号增加时,预计部署将需要一些时间,因为元数据消费工具必须在其他工具可以安全地开始生成新格式之前更新,或者 sdist 和 wheel 格式以及安装数据库定义需要更新,以支持并行提供多个版本的元数据。
现有工具将不会遵守此指南,直到它们更新以支持新的元数据标准,因此新语义将首先适用于假设的 2.x -> 3.0 过渡。对于 1.x -> 2.x 过渡,我们将采用一种方法,即工具继续生成现有的补充文件(如 entry_points.txt),此外还会使用新标准元数据格式(包括正式扩展机制)的等效文件。
切换到 JSON 兼容格式
旧的“键:值”格式变得越来越受限,各种复杂性,例如解析器需要知道哪些字段可以出现多次,哪些字段支持环境标记语法(带有一个可选的 `;` 将值与标记分开),甚至最终还可以选择在特定子字段中嵌入任意 JSON。
旧的序列化格式也不利于轻松转换为标准 Python 数据结构,以用于任何新的安装钩子 API,或未来扩展运行时导入器 API 以允许它们提供用于包含在安装数据库中的信息。
因此,我们采取了切换到 JSON 兼容元数据格式的步骤。这对 API 更好,工具也更容易正确解析和生成。更改元数据文件名也使得分发 1.x 和 2.x 元数据并行变得容易,极大地简化了迁移到新元数据格式的几个方面。
选择 pydist.json 作为首选文件名,是因为这些文件中描述的元数据适用于整个分发,而不是任何特定的构建。将来可能会定义其他元数据格式来存储只能在为特定目标环境构建二进制分发后才能确定的信息。
更改版本方案
有关版本化方案更改的详细理由,请参阅 PEP 440。
源代码标签
新的源代码标签支持旨在更清楚地表明对公共版本标识符的约束主要是为了帮助创建可靠的自动化依赖分析工具。项目可以自由地在内部使用任何他们喜欢的版本方案,只要他们能够将其转换为依赖分析工具能够理解的内容。
源代码标签还使得记录版本特定详细信息(如哈希或标签名称,允许从项目版本控制系统中重构发布)变得容易。
支持分发的选择性依赖
新的 extras 系统允许发行版声明可选行为,并使用依赖项字段来指示何时需要特定依赖项来支持该行为。它源自已经广泛使用的等效系统,作为 setuptools 的一部分,并允许将旧 setuptools 元数据的这一方面在新的元数据格式中准确表示。
与 setuptools 相比,extras 语法的增量是为了更容易地表达各种可能的依赖项组合,特别是那些与构建系统(可选支持运行测试套件)和开发系统相关的依赖项。
支持不同类型的语义依赖
通过 Extras 系统区分五种不同类型的依赖项,使项目能够选择性地指示是否需要特定依赖项来开发、构建、测试或使用发行版。
在上游 Python 特定元数据中支持这些区分的优点是,即使项目本身不关心这些区分,它们也可能更容易接受来自下游重新分发者的补丁,这些补丁会适当地分离字段。随着时间的推移,这应该可以更有效地控制特定依赖项在哪里以及何时最终被安装。
支持元数据扩展
新的扩展有效地允许将元数据命名空间的部分委托给其他项目,同时保留标准的整体格式元数据格式,以便于不支持特定扩展的发行版工具进行处理。
它还可以与新的 build extra 很好地结合,使发行版能够依赖于确实知道如何处理所选扩展和新 extras 机制的工具,从而允许将特定扩展的支持作为可选功能提供。
扩展的未来潜在用途包括声明其他项目的插件和为转换为 Linux 系统包提供提示。
包含声明扩展为必需的功能,主要是为了允许在初始采用元数据 2.0 规范一段时间后推迟定义元数据钩子扩展。如果一个版本需要运行 postinstall 钩子才能成功完成安装,那么早期版本的工具应该回退到从源安装,而不是从 wheel 文件安装然后无法运行预期的 postinstall 钩子。
附录 D:已推迟的功能
为了更好地优先处理迁移到新元数据标准的努力,一些潜在的有用功能已被刻意推迟。所有这些都反映了在新元数据中可能很方便提供的信息,但这些信息可以通过元数据扩展或在元数据 2.1 中轻松添加,而不会破坏元数据 2.0 已支持的任何用例。
一旦 pypi、setuptools、pip、wheel 和 distlib 项目支持创建和消费元数据 2.0,那么我们就可以重新审视创建元数据 2.1,并包含其中一些或全部附加功能。
标准扩展
旧元数据系统提供的一些信息已移至 PEP 459 中定义的标准扩展。
这允许核心依赖项元数据以更易于消费的格式发布,即使在这些扩展的详细信息尚未完全确定之前也可以进行。
改进项目废弃、重命名和合并的处理
此 PEP 的早期草案包括新的 Provides 和 Obsoleted-By 字段,用于更健壮的自动化通知和项目废弃、重命名和合并跟踪。
这不是依赖项管理系统的基本功能,已被无限期推迟,作为可能的未来元数据扩展。
MIME 类型注册
在此 PEP 被接受后的一段时间,我们可能会向 IANA 提交以下 MIME 类型注册请求:
application/vnd.python.pydist+json
甚至有可能我们可以将 vnd.python 命名空间在 PSF 的名下注册,而不是单独注册每个子格式。
环境标记中的字符串方法
在环境标记中支持至少 “.startswith” 和 “.endswith” 字符串方法将允许一些条件写得更自然。例如,"sys.platform.startswith('win')" 是标记特定于 Windows 的依赖项的一种更直观的方法,因为 "'win' in sys.platform" 是不正确的,因为 cygwin 和 64 位 Windows 仍显示为 win32 这一事实有点奇怪。
附录 E:已拒绝的功能
为了在表达能力上获得很小的收益而引入过多额外复杂性的几项潜在有用功能已被故意推迟。
条件和无条件依赖的单独列表
此 PEP 的早期版本为条件依赖项和非条件依赖项使用了单独的列表。这被证明在自动化工具中处理起来很麻烦,并且删除它也使 PEP 和元数据模式显著缩短,表明它实际上更难解释。
语义依赖的单独列表
此 PEP 的早期版本为测试、构建、文档和开发依赖项使用了单独的字段,而不是 extras 系统。这被证明在自动化工具中处理起来很麻烦,并且删除它也使 PEP 和元数据模式显著缩短,表明它实际上更难解释。
为过度精确的依赖声明引入摩擦
此 PEP 的早期版本试图在已发布的发行版中对过度严格的依赖项声明的不当使用引入摩擦。distutils-sig 上的讨论得出结论,这并不是一个足够严重的问题,可以直接在互操作性规范层面上解决,如果将来它确实成为一个问题,最好在项目上传到公共 Python 包索引时解决。
不允许在分发名称中使用下划线
Debian 实际上不允许名称中使用下划线,但考虑到在 Python 标识符用作 Python 发行版名称的常见做法,这对本规范来说似乎过于严格。Debian 侧将下划线转换为连字符的策略似乎很容易实现(并且考虑连字符和下划线等效的要求确保这样做不会引入任何名称冲突)。
允许在分发名称中使用 Unicode
此 PEP 故意避免遵循 Python 3 在任意 Unicode 标识符方面的路径,因为在软件分发用例中这样做会带来严重得多的安全隐患(它会比单纯的代码混淆打开更多的攻击向量)。
此外,现有的工具只有在限制名称为 ASCII 时才能正常工作,并且更改这一点将需要对链中的所有自动化工具进行大量工作。
在(遥远的)未来某个时候重新审视这个问题可能是合理的,但建立一个更可靠的软件分发系统本身就足够具有挑战性,而无需在混合中添加更多通用的 Unicode 标识符支持。
依赖于源代码标签
没有机制来表达对源标签的依赖项——它们仅包含在元数据中供项目内部参考。相反,必须根据公共版本或直接 URL 引用来表达依赖项。
备选依赖
此 PEP 的早期草案曾考虑允许在依赖项规范中使用列表代替常规字符串,以指示存在多种满足依赖项的方式。
如果至少一个单独的依赖项已经可用,那么整个依赖项将被视为已满足,否则第一个条目将被添加到依赖项集中。
替代依赖项规范示例
["Pillow", "PIL"]
["mysql", "psycopg2 >= 4", "sqlite3"]
然而,给出的两个示例都不太令人信服,因为 Pillow/PIL 风格的分支并不常见,并且数据库驱动程序用例可以通过 SQL Alchemy 定义的“支持的数据库驱动程序”元数据扩展来更好地满足,在该扩展中,一个项目依赖于 SQL Alchemy,然后在扩展中声明哪个数据库驱动程序经过上游项目兼容性检查。
环境标记中的兼容发布比较
PEP 440 定义了丰富的版本比较语法,在环境标记中与 python_version 和 python_full_version 结合使用可能很有用。但是,允许完整的语法意味着环境标记不再是 Python 的子集,而只允许某些比较则会引入另一个特殊情况来处理。
考虑到环境标记仅在元数据结构暗示更高级别的“或”的情况下使用,在那些不常见但需要此功能的情况下,要求使用多个与特定 Python 版本进行比较似乎更简单。
条件提供
在修订的元数据设计下,基于运行时功能或环境的条件“提供”将放在单独的“may_provide”字段中。然而,尚不清楚是否有任何用例可以这样做,因此除非有人能提供一个令人信服的用例(即使如此,该想法最早也要到元数据 2.1 才会重新考虑),否则该想法将被拒绝。
参考资料
本文档规定了元数据格式的版本 2.0。版本 1.0 在 PEP 241 中规定。版本 1.1 在 PEP 314 中规定。版本 1.2 在 PEP 345 中规定。
标准版本方案的初步尝试以及需要此类标准的理由可以在 PEP 386 中找到。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0426.rst