PEP 780 – ABI 功能作为环境标记
- 作者:
- Klaus Zimmermann <klaus_zimmermann at gmx.de>, Ralf Gommers <ralf.gommers at gmail.com>
- 发起人:
- Lysandros Nikolaou <lisandrosnik at gmail.com>
- 讨论至:
- Discourse 帖子
- 状态:
- 草案
- 类型:
- 标准跟踪
- 主题:
- 打包
- 创建日期:
- 2025年3月21日
- Python 版本:
- 3.14
- 发布历史:
- 2024年8月5日, 2025年3月26日
摘要
本PEP定义了通过一个新的 sys_abi_features
环境标记,将ABI功能用作项目依赖项的环境标记。PEP 508(后来移至依赖项指定符)引入了环境标记,以基于描述何时应使用依赖项的规则来指定依赖项。本PEP扩展了环境标记,以允许根据Python解释器的特定ABI功能来指定依赖项。为此,它定义了一组ABI功能,并指定了如何将它们作为新的标记变量 sys_abi_features
提供给环境标记。
动机
2015年,PEP 508建立了环境标记,用于根据环境条件指定依赖项。自由线程CPython的开发[1]强调了需要一个环境标记来区分解释器构建时使用的不同ABI功能。例如,目前无法使用环境标记区分GIL启用和自由线程CPython解释器。这导致了自由线程的采用及其增量推广的实际问题。当一个Python包与自由线程CPython兼容时,它还需要其所有构建和运行时依赖项都兼容。目前无法在元数据中精确捕获兼容的第一个依赖项版本,并且增加GIL启用构建的依赖项的最低版本通常是不希望的,因为它不必要地限制了包之间的兼容性。
在自由线程环境标记的Discourse帖子中讨论了这些具体问题的一些例子。
- Cython仅在其主分支中对自由线程提供(实验性)支持,并且被许多已经发布
cp313t
wheel的项目使用。选择错误的Cython版本会导致许多模糊的构建失败和运行时崩溃。如果元数据能够表达这一点,将会很有益处(参见为自由线程Python要求Cython预发布版)。 - CFFI尚未支持自由线程,并且Armin Rigo(其中一位维护者)表示,分叉
cffi
,实现自由线程支持,然后仅在功能“经过合理测试(无论是作为测试,还是在这种情况下,通过在各种其他项目中实际使用进行测试)”后,再以一个大型PR的形式返回CFFI项目,可能是一个好主意。有许多项目依赖于cffi
。它们可能乐意仅为自由线程而开始依赖于一个分叉,但是为>=3.13或所有Python版本依赖于一个分叉似乎是一个更大的要求,并且对分发打包者来说更具破坏性。
尽管这些具体示例可能会在今年晚些时候通过 Cython 和 CFFI 发布兼容版本来解决,但相同的问题将在堆栈的更高层重复出现。自由线程的推广预计将需要数年时间,而一个自由线程环境标记将使推广过程显著简化。
另一个尚未被环境标记覆盖的重要ABI特性是解释器的位数。在大多数情况下,sys_platform
或 platform_system
标记就足够了,因为每个平台通常只使用一种位数。然而,在 Windows 上情况并非如此:x86-64 Windows 上广泛使用 32 位和 64 位 Python 解释器。无法区分两者可能与提供编译扩展的包相关。例如,SciPy 不提供 win32
wheels(由于缺乏支持 Fortran 的合适 32 位编译器工具链而无法提供)。这些缺失的 wheels 可能会很尴尬,尤其是对于 SciPy 只是可选依赖项的项目。在这种情况下,能够指定除非解释器是 Windows 上的 32 位,否则需要 SciPy(参见除非在 32 位 win32 上,否则要求 SciPy)将很有用,以避免因缺少 wheels 而导致的从源代码安装失败。
基本原理
本PEP的目的是引入其核心功能,同时尽量减少对现有生态系统的影响。PEP 508中提出的现有语法易于直接扩展以包含新的环境标记。
对自由线程Python的展望
PEP 703,即自由线程的已接受提案,指出自由线程Python的推广应逐步进行,Python指导委员会在PEP 703接受帖子中对此进行了澄清,这意味着一个跨多个版本的三个阶段过程。因此,重要的是要确保本PEP中的机制适用于自由线程或非自由线程可能是默认或唯一选项的Python解释器。
在撰写本文时,自由线程 Python 处于第一阶段:实验阶段。在此阶段,迫切需要提出的环境标记,以帮助随着包作者逐步添加支持而向自由线程 Python 过渡。
随着支持包数量的增加,特别是在第二阶段:支持但不默认阶段,我们仍然预计对环境标记有强烈的需求,以帮助过渡。
当自由线程Python进入第三阶段:默认阶段时,对环境标记的需求将减少,尽管目前尚不清楚GIL启用的Python是否会完全淘汰(它可能仍然作为非标准构建选项可用)。如果它持续存在,可能会出现对ABI特性检测的逆向需求。
事实上,在所有三个阶段中,包作者可能都需要根据 ABI 功能选择其依赖项的特定版本,随着时间的推移,从默认启用 GIL 转移到默认自由线程。
ABI 功能的设计考虑到了这一点,以确保在不断变化的 Python 生态系统中,在可预见的未来保持有用性和简洁性。
与其他PEP的关系
本PEP扩展了带有ABI特性集语义的环境标记。PEP 751包含了一个类似的扩展,用于锁定文件特定的环境标记;尽管两者是独立开发的,但在新的集合语义方面它们是兼容的。
规范
本文档中的关键词“必须”、“不得”、“必需”、“应”、“不应”、“建议”、“不建议”、“推荐”、“可以”和“可选”应按照RFC 2119中的描述进行解释。
ABI功能
ABI 特性是 Python 解释器的内在属性,表示为简单易懂的字符串。然而,并非所有特性都同样适用于所有 Python 解释器或 Python 版本。例如,自由线程解释器和 GIL 启用解释器之间的区别仅与 CPython 3.13 及更高版本相关,但解释器的位数与所有解释器都相关。
所有解释器**必须**按所述处理以下 ABI 功能。仅限于特定解释器的 ABI 功能**不得**由其他解释器提供。这些功能分为组,每个组**必须**正好存在一个功能,除非该组被标记为可选,在这种情况下,**必须**最多存在一个功能。
free-threading
或gil-enabled
(仅限 CPython)- 如果Python解释器是自由线程的,则
free-threading
特性**必须**存在,并且gil-enabled
特性**不得**存在。否则,gil-enabled
特性**必须**存在,并且free-threading
特性**不得**存在。 debug
(仅限 CPython,可选)- 此ABI功能保留用于CPython的
--with-pydebug
构建。如果解释器是具有Py_DEBUG
功能的CPython解释器,则debug
功能**必须**存在。在POSIX系统上,这对应于Python表达式"d" in sys.abiflags
。 32-bit
或64-bit
(可选)- 解释器的位数,即它是 32 位还是 64 位构建[2]。如果位数未知,或者既不是 32 位也不是 64 位,则此功能**不得**存在。
sys_abi_features
环境标记
为了使 ABI 功能在依赖项规范中可用,新的环境标记变量 sys_abi_features
将添加到依赖项指定符的格式中。
为此,我们需要扩展PEP 508中规定并在依赖项指定符中维护的语法,并记录可能的值。
语法通过如下扩充 env_var
的定义来包含 sys_abi_features
标记变量
env_var = ('python_version' | 'python_full_version' |
'os_name' | 'sys_platform' | 'platform_release' |
'platform_system' | 'platform_version' |
'platform_machine' | 'platform_python_implementation' |
'implementation_name' | 'implementation_version' |
'sys_abi_features' |
'extra' # ONLY when defined by a containing layer
)
与语法一样,依赖项指定符中的环境标记概述表也进行了扩充,增加了以下一行
标记 | Python 等效项 | 示例值 |
---|---|---|
sys_abi_features |
没有直接等效项 | {'free-threading', '64-bit'} , {'gil-enabled', 'debug', '32-bit'} |
通过这些补充,ABI 功能可以在依赖项规范中通过 in
运算符测试功能的存在,或者通过 not in
运算符测试功能的缺失。
示例
为自由线程Python要求Cython预发布版
若要仅为自由线程Python解释器要求Cython的预发布版本,可以使用以下依赖项规范
cython >3.1.0a1; "free-threading" in sys_abi_features
cython ==3.0.*; "free-threading" not in sys_abi_features
除非在32位 win32
上,否则要求SciPy
若要要求 SciPy,除非在 Windows 上的 32 位解释器上,可以使用以下依赖项规范
scipy; platform_system != "Windows" or "32-bit" not in sys_abi_features
为具有调试功能的自由线程解释器要求NumPy
若要仅为具有调试功能的自由线程解释器要求 NumPy,可以使用以下依赖项
numpy; "free-threading" in sys_abi_features and "debug" in sys_abi_features
向后兼容性
这是对现有环境标记的纯粹扩展,不影响现有环境标记或依赖规范,因此没有直接的向后兼容性问题。
然而,这项功能的引入对许多生态系统工具产生了影响,尤其是那些试图支持检查 pyproject.toml
和 requirements.txt
中数据的工具。
审计和更新工具
各种工具都理解以 requirements.txt
文件形式表示的 Python 依赖数据。(例如,Dependabot、Tidelift 等)
此类工具检查依赖数据,并在某些情况下提供工具辅助或完全自动化的更新。我们预计,起初没有任何此类工具会支持新的环境标记,广泛的生态系统支持可能需要数月甚至数年才能实现。
因此,新环境标记的用户在开始使用它们时,其工作流程和工具支持将有所下降。任何新的依赖数据编码位置和方式标准都是如此。
安全隐患
本PEP引入了在项目中指定依赖信息的新语法。然而,它并未引入新的机制来处理或解决依赖关系。
因此,除了任何可能已经用于安装依赖项的工具所固有的安全问题之外,它不带来其他安全问题——即,此处可以指定恶意依赖项,就像它们可以在 requirements.txt
文件中指定一样。
如何教授此内容
环境标记的使用已得到充分确立和主要通过依赖项指定符进行传达。新的环境标记可以在同一文档中引入。此外,对于包作者和用户,可以在Python自由线程指南中提供特定于自由线程的指导。
参考实现
环境标记的参考实现在 packaging
库的一个分支中提供,位于 ABI 功能的环境标记。
一个演示包也已提供。
由于pip
内部使用了packaging
的内嵌副本,我们还提供了一个打补丁版本的pip,它将内嵌的packaging
替换为上面链接的参考实现。
被拒绝的想法
扩展机制
在早期关于该主题的讨论中(自由线程的环境标记),提出了环境标记的通用扩展机制的想法。虽然如果将来出现新的环境标记需求时,可以避免整个PEP流程很吸引人,但存在两个主要挑战。
首先,一个完全动态的机制将给依赖于依赖规范静态分析的工具带来困难。
这意味着,即使采用动态机制,新的环境标记仍可能需要在PEP中明确说明。
其次,引入动态机制将要求打包库中实现更复杂,这将与当前方法有显著不同。
未解决的问题
其他环境标记
如果现在需要其他环境标记,则本 PEP 可以扩展以包含它们。
其他工具
参考实现基于 packaging
库和 pip
。我们已经确认这允许使用多个构建后端构建和安装包。可能应该将其他工具添加到参考实现中。
脚注
致谢
感谢 Filipe Laíns 提出 abi_features
属性的建议,并感谢 Stephen Rosen 提供了 PEP 735 的向后兼容性部分,该部分作为本 PEP 相应部分的模板。
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0780.rst