C API ABI 的稳定性¶
除非另有文档说明,Python 的 C API 将遵循 PEP 387 所描述的向下兼容策略。 对它的大部分改变都是源代码级兼容的(通常只会增加新的 API)。改变现有 API 或移除 API 只会在弃用期结束之后或需修复严重问题时才会发生。
CPython 的应用程序二进制接口(ABI)可以跨微版本向上和向下兼容(在以相同方式编译的情况下,参见下文 平台的考虑 一节)。因此,针对 Python 3.10.0 编译的代码将适用于 3.10.8,反之亦然,但对于 3.9.x 和 3.11.x 则需要单独编译。
存在具有不同稳定性预期的两个 C API 层次:
不稳定 API,可能在次要版本中发生改变而没有弃用期。它的名称会以
PyUnstable前缀来标记。受限 API,将会在多个次要版本间保持兼容。当定义了
Py_LIMITED_API时,将只有这个子集会从Python.h对外公开。
这些将在下文中更详细地讨论。
带有一个下划线前缀的名称,如 _Py_InternalState,是可能不经通知就改变甚至是在补丁发布版中改变的私有 API。 如果你需要使用这样的 API,请考虑联系 CPython 开发团队 来讨论为你的应用场景添加公有 API。
不稳定 C API¶
任何名称带有 PyUnstable 前缀的 API 都将对外公开 CPython 的实现细节,并可能不加弃用警告即在次要版本中发生改变(例如从 3.9 到 3.10)。但是,它不会在问题修正发布版中改变(例如从 3.10.0 到 3.10.1)。
它通常是针对专门的,低层级的工具如调试器等。
使用此 API 的项目需要跟随 CPython 开发进程并花费额外的努力来适应改变。
稳定应用程序二进制接口¶
Python 的 稳定 ABI 允许扩展在不重新编译的情况下兼容多个 Python 版本。
备注
为简单起见,本文档会讨论 扩展,但稳定 ABI 对 API 的所有使用方式都同样适用 —— 例如嵌入 Python。
有两种稳定 ABI:
abi3于 Python 3.2 中引入,兼容 CPython 的 非自由线程 构建。abi3t于 Python 3.15 中引入,兼容 CPython 的 自由线程 构建。它具有比abi3更严格的 API 限制。Added in version 3.15:
abi3t是在 PEP 803 中添加的
扩展可以同时针对 abi3 和 abi3t 二者 进行编译;其结果将同时兼容 Python 的自由线程构建和非自由线程构建。目前,与仅针对 abi3t 编译相比,这样做没有缺点。
每个稳定 ABI 都使用 Python 版本号的前两个数字作为版本号。例如,稳定 ABI 3.14 对应 Python 3.14。针对稳定 ABI 3.x 编译的扩展与 Python 3.x 及更高版本保持 ABI 兼容。
以稳定 ABI 为目标的扩展只能使用 C API 的一个受限子集。该子集称为 受限 API;其内容在 下文列出。
在 Windows 上,使用稳定 ABI 的扩展应当链接到 python3.dll,而不是链接到版本专属库,例如 python39.dll。这个库只会公开相关符号。
在某些平台上,Python 会查找并加载名称带有 abi3 或 abi3t 标签的共享库文件(例如 mymodule.abi3.so)。自由线程 解释器只识别 abi3t 标签,而非自由线程解释器会优先使用 abi3,但会回退到 abi3t。因此,同时兼容这两种 ABI 的扩展应使用 abi3t 标签。
Python 不一定会检查它所加载的扩展是否具有兼容的 ABI。建议扩展作者使用 Py_mod_abi 槽位或 PyABIInfo_Check() 函数添加检查,但用户(或其打包工具)最终需要负责确保,例如,针对稳定 ABI 3.10 构建的扩展不会被安装到更低版本的 Python 中。
稳定 ABI 中的所有函数都以函数的形式存在于 Python 的共享库中,而不是仅以宏的形式存在。这使得它们可用于不使用 C 预处理器的语言,包括 Python 的 ctypes。
针对稳定 ABI 编译¶
备注
构建工具(例如 meson-python、scikit-build-core 或 Setuptools)通常具有用于设置宏,并将这些宏与扩展文件名和其他元数据同步的机制。如果存在这样的机制,应优先使用它,而不是手动定义宏。
本节的其余部分主要与工具作者以及手动编译扩展的人相关。
参见
Python 打包用户指南中的 推荐工具列表
要针对稳定 ABI 编译,请将以下一个或两个宏定义为你的扩展应支持的最低 Python 版本,格式为 Py_PACK_VERSION。通常,你应选择一个具体值,而不是正在用于编译的 Python 头文件版本。
这些宏必须在包含 Python.h 之前定义。由于此时 Py_PACK_VERSION 尚不可用,你需要直接使用数值。作为参考,几个较新 Python 版本对应的值如下:
0x30b0000 /* Py_PACK_VERSION(3.11) */
0x30c0000 /* Py_PACK_VERSION(3.12) */
0x30d0000 /* Py_PACK_VERSION(3.13) */
0x30e0000 /* Py_PACK_VERSION(3.14) */
0x30f0000 /* Py_PACK_VERSION(3.15) */
0x3100000 /* Py_PACK_VERSION(3.16) */
当定义其中一个宏时,Python.h 将只公开与给定稳定 ABI 兼容的 API —— 即 受限 API,以及一些需要对编译器可见、但不应直接使用的定义。当二者都被定义时,Python.h 将只公开同时兼容这两种稳定 ABI 的 API。
这两个宏都会指定一个目标 ABI;命名风格不同是出于向后兼容。
历史说明
你也可以将 Py_LIMITED_API 定义为 3。 这与 0x03020000 (Python 3.2,即引入稳定 ABI 的版本) 的作用相同。
当二者都被定义时,Python.h 可能会,也可能不会,重新定义 Py_LIMITED_API 以匹配 Py_TARGET_ABI3T。
在 自由线程构建 上 —— 也就是定义了 Py_GIL_DISABLED 时 —— Py_TARGET_ABI3T 默认取 Py_LIMITED_API 的值。这意味着有两种方式可以同时针对 abi3 和 abi3t 构建:
同时定义
Py_LIMITED_API和Py_TARGET_ABI3T,或者只定义
Py_LIMITED_API,并且:在 Windows 上,定义
Py_GIL_DISABLED;在其他系统上,使用 Python 自由线程构建的头文件。
稳定 ABI 的范围与性能¶
稳定 ABI 的目标是允许完成完整 C API 能做到的所有事情,但可能会带来性能损失。通常,与稳定 ABI 兼容会要求对扩展的源代码做出一些修改。
例如,虽然 PyList_GetItem() 可用,但它的“非安全”宏变体 PyList_GET_ITEM() 不可用。该宏可能更快,因为它可以依赖列表对象的版本专属实现细节。
再举一例,当 不 针对稳定 ABI 编译时,某些 C API 函数会被内联或被宏替换。针对稳定 ABI 编译会禁用这种内联,使其在 Python 的数据结构得到改进时仍保持稳定,但可能会降低性能。
通过省略 Py_LIMITED_API 或 Py_TARGET_ABI3T 定义,可以将与稳定 ABI 兼容的源代码编译为版本专属 ABI。这样就可以将一个可能更快的版本专属扩展,与一个针对稳定 ABI 编译的版本一同分发 —— 后者较慢,但兼容性更好,可作为回退方案。
稳定 ABI 注意事项¶
请注意,针对稳定 ABI 编译 并不 完全保证代码会与预期的 Python 版本兼容。稳定 ABI 防止的是 ABI 问题,例如因缺少符号导致的链接器错误,或因结构体布局或函数签名改变导致的数据损坏。然而,Python 中的其他改变可能会改变扩展的 行为。
有一个问题是 Py_TARGET_ABI3T 和 Py_LIMITED_API 宏无法防范的:使用在较低 Python 版本中无效的参数来调用函数。例如,假设某个函数开始接受 NULL 作为参数。在 Python 3.9 中,NULL 现在会选择一种默认行为,但在 Python 3.8 中,该参数会被直接使用,从而导致 NULL 解引用并崩溃。结构体字段也存在类似情况。
由于这些原因,我们建议使用扩展所支持的 所有 Python 次要版本来测试该扩展。
我们还建议查看所使用 API 的全部文档以检查其是否显式指明为受限 API 的一部分。即使定义了 Py_LIMITED_API,少数私有声明还是会出于技术原因(或者甚至是作为程序缺陷在无意中)被暴露出来。
还要注意,虽然使用 Py_LIMITED_API 3.8 编译意味着扩展应当能在 Python 3.12 上 加载,也能使用 Python 3.12 编译,但同一份源代码不一定能在将 Py_LIMITED_API 设为 3.12 时完成编译。通常,只要稳定 ABI 保持稳定,受限 API 的某些部分可能会被弃用并移除。
平台的考虑¶
ABI 稳定性不仅取决于 Python,还取决于所使用的编译器、底层库和编译器选项。就 稳定 ABI 而言,这些细节定义了一个“平台”。它们通常取决于操作系统类型和处理器架构
每个特定的 Python 分发方都有责任确保特定平台上的所有 Python 版本都以不会破坏稳定 ABI 或版本专属 ABI 的方式构建。来自 python.org 的 Windows 和 macOS 发布版以及许多第三方分发版就是这种情况。
ABI 检查¶
Added in version 3.15.
Python 包含一种基本的 ABI 兼容性检查。
这种检查并不全面。它只能防范将不兼容模块安装给错误解释器的常见情况。它也不会考虑 平台不兼容性。它只能在扩展成功加载后进行。
尽管存在这些限制,仍建议扩展模块使用此机制,以便可检测到的不兼容性会引发异常,而不是导致崩溃。
大多数模块可以通过 Py_mod_abi 槽位和 PyABIInfo_VAR 宏来使用此检查,例如:
PyABIInfo_VAR(abi_info);
static PyModuleDef_Slot mymodule_slots[] = {
{Py_mod_abi, &abi_info},
...
};
完整 API 将在下文针对高级使用场景进行说明。
-
int PyABIInfo_Check(PyABIInfo *info, const char *module_name)¶
- 属于 稳定 ABI 自 3.15 版起.
验证给定的 info 是否与当前正在运行的解释器兼容。
成功时返回 0。失败时引发异常并返回 -1。
如果 ABI 不兼容,引发的异常将是
ImportError。module_name 参数可以为
NULL,也可以指向一个以 NUL 终止、使用 UTF-8 编码、用于错误消息的字符串。请注意,如果 info 描述的是当前代码所使用的 ABI(例如由
PyABIInfo_VAR定义),则使用任何其他 Python C API 都可能导致崩溃。尤其是,检查所引发的异常是不安全的。Added in version 3.15.
-
PyABIInfo_VAR(NAME)¶
- 属于 稳定 ABI 自 3.15 版起.
定义一个具有给定 NAME 的静态
PyABIInfo变量,用于描述当前代码将使用的 ABI。此宏会展开为:static PyABIInfo NAME = { 1, 0, PyABIInfo_DEFAULT_FLAGS, PY_VERSION_HEX, PyABIInfo_DEFAULT_ABI_VERSION }
Added in version 3.15.
-
type PyABIInfo¶
- 属于 稳定 ABI (包括所有成员) 自 3.15 版起.
-
uint16_t flags¶
此字段通常设为以下宏:
-
PyABIInfo_DEFAULT_FLAGS¶
- 属于 稳定 ABI 自 3.15 版起.
默认标志,基于
Py_LIMITED_API和Py_GIL_DISABLED等宏的当前值。
或者,可以将此字段设为以下标志,并通过按位或组合。未使用的位必须设为零。
ABI 变体 —— 以下之一:
自由线程兼容性 —— 以下之一:
-
PyABIInfo_FREETHREADED¶
- 属于 稳定 ABI 自 3.15 版起.
指定与 CPython 的 自由线程构建 兼容的 ABI。(即使用
--disable-gil编译,并且sys.abiflags中带有t的构建)
-
PyABIInfo_GIL¶
- 属于 稳定 ABI 自 3.15 版起.
指定与 CPython 的非自由线程构建兼容的 ABI(即 未 使用
--disable-gil编译的构建)。
-
PyABIInfo_DEFAULT_FLAGS¶
-
uint32_t build_version¶
用于构建代码的 Python 头文件版本,采用
PY_VERSION_HEX所使用的格式。可以将其设为
0,以跳过与此字段相关的任何检查。此选项主要面向不直接使用 CPython 头文件,并且不模拟其某个特定版本的项目。
-
uint32_t abi_version¶
ABI 版本。
对于稳定 ABI,此字段应为
Py_LIMITED_API或Py_TARGET_ABI3T的值。如果二者都被定义,则使用较小的值。(如果Py_LIMITED_API为3,则使用 Py_PACK_VERSION(3, 2) 而不是3。)否则,应将其设为
PY_VERSION_HEX。也可以将其设为
0,以跳过与此字段相关的任何检查。-
PyABIInfo_DEFAULT_ABI_VERSION¶
- 属于 稳定 ABI 自 3.15 版起.
此字段应使用的值,基于
Py_LIMITED_API、PY_VERSION_HEX和Py_GIL_DISABLED等宏的当前值。
-
PyABIInfo_DEFAULT_ABI_VERSION¶
Added in version 3.15.
-
uint16_t flags¶
受限 API 的内容¶
这是 Python 3.16 的 受限 API 权威列表:
PyErr_Display()PyWeakReferencePy_FileSystemDefaultEncodeErrorsPy_FileSystemDefaultEncodingPy_HasFileSystemDefaultEncodingPy_UTF8ModePy_intptr_tPy_uintptr_tssizessizeargfuncssizessizeobjargprocsymtable