Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 803 – 自由线程构建的稳定 ABI

作者:
Petr Viktorin <encukou at gmail.com>
讨论至:
Discourse 帖子
状态:
草案
类型:
标准跟踪
要求:
703, 793, 697
创建日期:
2025年8月19日
Python 版本:
3.15
发布历史:
2025年9月8日

目录

摘要

稳定 ABI 的 3.15 版本将与自由线程和启用 GIL 的构建兼容。为实现此目的,PyObject 内部结构和相关 API 将从 Limited API 的 3.15 版本中移除,需要迁移到新的 API 来完成定义模块和大多数类的常见任务。

使用 Limited API 3.15 及更高版本构建的二进制分发 (wheel) 应使用 ABI 标签 abi3.abi3t

术语

本 PEP 使用“启用 GIL 的构建”作为“自由线程构建”的反义词,即不带 Py_GIL_DISABLED 构建的解释器或扩展。

动机

稳定 ABI 目前不适用于自由线程构建。当定义 Py_LIMITED_API 时,扩展将无法构建,并且为 CPython 的启用 GIL 构建的扩展将在自由线程构建上无法加载(或崩溃)。

PEP 779接受帖中,指导委员会表示“期望为 Python 3.15 准备和定义自由线程的稳定 ABI”。

本 PEP 提出了自由线程的稳定 ABI。

背景

Python 的稳定 ABI,如 PEP 384PEP 652 中所定义,提供了一种编译扩展模块的方法,这些模块可以在 CPython 解释器的多个次要版本上加载。一些项目使用它来限制每个版本需要构建和分发的 wheels(二进制工件)的数量,和/或使其更容易使用 Python 的预发布版本进行测试。

随着自由线程构建 (PEP 703) 有望最终成为默认 (PEP 779),我们需要一种方法使稳定 ABI 适用于这些构建。

为了针对稳定 ABI 进行构建,扩展必须使用一个 Limited API,即 CPython 暴露的函数、结构等的一个子集。Limited API 是有版本的,针对 Limited API 3.X 构建会产生一个与 CPython 3.X 和 任何 更高版本 ABI 兼容的扩展(尽管 CPython 中的错误有时会在实践中导致不兼容)。此外,Limited API 并非“稳定”:更新的版本可能会删除旧版本中的 API。

本 PEP 提出了迄今为止最重要的此类删除。

基本原理

本 PEP 中的设计有几个假设

一个 ABI
一个编译的扩展模块应同时支持自由线程和启用 GIL 的构建。
目前不向后兼容
新的 Limited API 不支持 CPython 3.14 及以下版本。需要此支持的项目可以专门为 3.14 自由线程解释器和旧版稳定 ABI 构建单独的扩展。

但是,我们不会阻止将兼容性扩展到 CPython 3.14 及以下版本的可能性。

API 更改是可以接受的
新的 Limited API 可能要求扩展作者对其代码进行重大更改。暂时无法这样做的项目可以继续使用 Limited API 3.14,这将产生仅与启用 GIL 的构建兼容的扩展。
无需额外配置
我们不引入影响可用 API 和 ABI 兼容性的新“旋钮”。

规范

不透明 PyObject

Limited API 3.15 将

影响

使 PyObjectPyVarObjectPyModuleDef 结构不透明意味着

  • 它们的字段不能直接访问。

    例如,扩展必须使用 Py_TYPE(o) 而不是 o->ob_type。这种用法已有一段时间是首选做法。

  • 它们的尺寸和对齐方式将不可用。诸如 sizeof(PyObject) 之类的表达式将不再起作用。
  • 它们不能嵌入到其他结构中。这主要影响扩展定义类型的实例结构,这些结构需要使用 PEP 697 中添加的 API 定义——也就是说,使用一个 不带 PyObject(或其他基类结构)开头的 struct,并使用 PyObject_GetTypeData() 调用来访问内存。
  • 不能创建这些类型的变量。这主要影响定义扩展模块所需的静态 PyModuleDef 变量。扩展将需要切换到 PEP 793 中添加的 API。

以下函数在实践中将变得不可用(在新的 Limited API 中),因为扩展无法为它们创建有效、静态分配的输入。为方便扩展开发人员过渡,它们尚未从 Limited API 中移除

新的导出钩子 (PEP 793)

本 PEP 的实现要求接受 PEP 793 (PyModExport):C 扩展模块的新入口点),以提供定义扩展模块的新“导出钩子”。使用新钩子将在 Limited API 3.15 中成为强制性要求。

运行时 ABI 检查

用户——或者说他们用于构建和安装扩展的工具——将继续负责不将不兼容的扩展放在 Python 的导入路径上。这个决定是合理的,因为工具通常比 CPython 能检查的元数据丰富得多。

然而,CPython 将通过一个新的 模块槽 形式,包含基本的 ABI 信息,增加一层防御,以防止过时或配置错误的工具,或人为错误。当模块加载时,将检查此信息,并拒绝不兼容的扩展。具体细节留给 C API 工作组(参见问题 72)。

此槽将在 PEP 793 中添加的新导出钩子中成为 强制性。(该 PEP 目前说“没有必需的槽”;它将被更新。)

检查旧版 abi3

此外,在自由线程构建中,PyModuleDef_Init() 将检测使用预自由线程稳定 ABI 的扩展,当加载时发出信息性消息, 引发异常。(实现说明:将在引发异常之前打印消息,因为尝试使用不兼容 ABI 处理异常的扩展可能会崩溃并丢失异常消息。)

这种对旧版 abi3 的检查依赖于内部位模式,如果内部对象布局需要更改,则可能在未来的 CPython 版本中移除。

abi3t wheel 标签

使用与自由线程 CPython 构建兼容的稳定 ABI 的 Wheel 应使用新的 ABI 标签:abi3t。选择该名称是为了反映该 ABI 与 abi3 相似,但进行了支持自由线程所需的更改(在现有特定版本 ABI 标签(如 cp314t)中使用字母 t)。

由于使用 Limited API 3.15 构建的 wheel 将与启用 GIL 的构建和自由线程构建兼容,因此它们应使用压缩 ABI 标签集 abi3.abi3t

新API

实施本 PEP 将使构建可成功加载到自由线程 Python 上的扩展成为可能,但不一定是没有 GIL 的线程安全的扩展。

允许没有 GIL 的线程安全的 Limited API——大概是 PyMutexPyCriticalSection 和类似的东西——将通过 C API 工作组添加,或在后续 PEP 中添加。

向后和向前兼容性

由于需要使用 PEP 793 中添加的新 PyModExport API,Limited API 3.15 将不向后兼容旧版 CPython 版本。

无法切换的扩展作者可以继续使用 Limited API 3.14 及以下版本。为了与自由线程构建兼容,他们可以使用特定版本的 ABI 进行编译——例如,在 CPython 3.15 上编译而不定义 Py_LIMITED_API

Limited API 3.15 将向前兼容未来版本的 CPython 3.x。旧版 Limited API(即 3.14 及以下版本)将继续向前兼容启用 GIL 的 CPython 3.x 构建,从引入给定 Limited API 的版本开始。

兼容性概述

下表总结了 wheel 标签与 CPython 解释器的兼容性。“GIL”代表启用 GIL 的解释器;“FT”代表自由线程的解释器。

Wheel 标签 3.14 (GIL) 3.14 (FT) 3.15 (GIL) 3.15 (FT) 3.16+ (GIL) 3.16+ (FT)
cp314-cp314
cp314-cp314t
cp314-abi3
cp315-cp315
cp315-cp315t
cp315-abi3
cp315-abi3.abi3t

下表总结了使用给定解释器和 Py_LIMITED_API 宏构建的扩展应使用的 wheel 标签

要获取 wheel 标签... 在...上编译 Py_LIMITED_API 设置为... 注意
cp314-cp314 3.14 (GIL) (未设置) 现有
cp314-cp314t 3.14 (FT) (未设置) 现有
cp314-abi3 3.14+ (GIL) PY_PACK_VERSION(3, 14) 现有
cp315-cp315 3.15 (GIL) (未设置) 继续
cp315-cp315t 3.15 (FT) (未设置) 继续
cp315-abi3 3.15+ (GIL) PY_PACK_VERSION(3, 15) 已停用
cp315-abi3.abi3t 3.15+ (任何) PY_PACK_VERSION(3, 15)

注意 列中的值

  • 现有:Wheel 标签目前正在使用中
  • 继续:Wheel 标签延续现有方案
  • 已停用:Wheel 标签延续现有方案,但会不鼓励使用。旧工具可能仍会生成它。
  • :本 PEP 中提出。

安全隐患

未知。

如何教授此内容

需要一份移植指南来解释如何迁移到 PEP 697(扩展不透明类型的有限 C API)和 PEP 793PyModExport)中添加的 API。

参考实现

本 PEP 结合了若干单独实现的组件

  • 不透明 PyObject 在定义 _Py_OPAQUE_PYOBJECT 宏后可在 CPython 主分支中使用。在 GitHub 拉取请求 python/cpython#136505 中实现。
  • 有关 PyModExport,请参阅 PEP 793
  • 版本检查槽已在 GitHub 拉取请求 python/cpython#137212 中实现。
  • 对旧版 abi3 的检查已在 GitHub 拉取请求 python/cpython#137957 中实现。
  • 对于 wheel 标签,尚未有实现。
  • 移植指南尚未编写。

被拒绝的想法

为自由线程添加替代的稳定 ABI

有以下可能性

  • 添加一个新的稳定 ABI (“abi3t”),专门用于自由线程,它将与现有的 abi3 不兼容。扩展无需代码更改即可针对 abi3t,构建将与自由线程 CPython (3.14 及以上) 兼容。
  • 定义一个附加宏 (“Py_OPAQUE_PYOBJECT”),这将使 PyObject 像本 PEP 中一样不透明。扩展需要像本 PEP 中一样的代码更改,并且像本 PEP 中一样,编译后的扩展 (“abi3.abi3t”) 将与 CPython 3.15+ 的所有构建兼容。

此方案因过于复杂而被拒绝。它还将使 PyObject 的自由线程内存布局成为稳定 ABI 的一部分,从而阻止未来的调整。

与 CPython 3.14 兼容的垫片

阻止与 Python 3.14 兼容的主要问题是,由于 PyObjectPyModuleDef 不透明,初始化扩展模块是不可行的。解决方案 PEP 793 仅在 Python 3.15 中添加。

可以通过利用 3.14 ABI(自由线程和启用 GIL)是“冻结”的事实来解决这个问题,因此扩展可以查询正在运行的解释器,对于 3.14,使用与检测到的构建的 PyModuleDef 相对应的 struct 定义。

在这一点上,这对于 CPython 的 Limited API 来说过于繁重,难以支持和测试,但未来可能会允许。

使用 Python wheel 标签确定兼容性

本 PEP 的先前版本避免添加新的 wheel 标签 (abi3t),并指定如果 Python 标签cp315 或更高版本,则标记为 abi3 的 wheel 将与自由线程兼容。

这样的方案适用于本 PEP,但它无法表达扩展与 CPython 3.14 或更低版本的启用 GIL 和自由线程构建都兼容。添加一个新的显式标签意味着,如果 我们将来允许构建此类 wheel,打包工具应该不需要额外的更改来支持它们。它们将被标记为 cp314-abi3.abi3t

添加 abi4 wheel 标签

我们可以用 abi4 代替 abi3t 作为 wheel ABI 标签。“版本提升”并使用 abi4。在 wheel 标签中,区别主要在于外观。

然而,本 PEP 没有提出的一个问题是更改 文件名 标签:扩展名将以 .abi3.so 等扩展名命名。在保持与 GIL 启用构建兼容的同时更改此名称将是一个不必要的技术更改。

在 wheel 标签中使用 abi3.abi4 而文件名中只使用 .abi3 会比 abi3.abi3t.abi3 看起来更不一致。


来源:https://github.com/python/peps/blob/main/peps/pep-0803.rst

上次修改:2025-09-19 13:35:24 GMT