C API 的稳定性¶
除非另有文档说明,Python 的 C API 将遵循 PEP 387 所描述的向下兼容策略。 对它的大部分改变都是源代码级兼容的(通常只会增加新的 new 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 开发进程并花费额外的努力来适应改变。
应用程序二进制接口的稳定版¶
简单起见,本文档只讨论了 扩展,但受限 API 和稳定 ABI 对于 API 的所有用法都能发挥相同的作用 – 例如嵌入版的 Python 等。
受限 C API¶
Python 3.2 引入了 受限 API,它是 Python 的 C API 的子集。 只使用受限 API 的扩展可以一次编译即可在多个 Python 版本上加载。 受限 API 内容 如下所示。
-
Py_LIMITED_API¶
请在包括
Python.h
之前定义这个宏以选择只使用受限 API,并选择受限 API 的版本。将
Py_LIMITED_API
定义为对应于你的扩展所支持的最低 Python 版本的PY_VERSION_HEX
值。 扩展将与从指定版本开始的所有 Python 3 发布版保持 ABI 兼容,并可使用到该版本为止所引入的受限 API。不直接使用
PY_VERSION_HEX
宏,而是碍编码一个最小的次要版本(例如0x030A0000
表示 Python 3.10)以便在使用未来的 Python 版本进行编译时保持稳定。你还可以将
Py_LIMITED_API
定义为3
。 其效果与0x03020000
相同(即 Python 3.2,引入受限 API 的版本)。
稳定 ABI¶
为启用此特性,Python 提供了一个 稳定 ABI:即一组将跨 Python 3.x 各个版本保持 ABI 兼容的符号集合。
备注
稳定 ABI 将防止多种 ABI 问题,如由于缺失符号导致的链接器错误或由于结构体布局或函数签名中的变化导致的数据损坏。 不过,Python 中的其他修改可能改变扩展的 行为。 请参阅 Python 的向下兼容策略 (PEP 387) 了解详情。
稳定 ABI 包含在 受限 API 中对外公开的符号,但还包含其他符号 – 例如,为支持旧版本受限 API 所需的函数。
在 Windows 上,使用稳定 ABI 的扩展应当被链接到 python3.dll
而不是版本专属的库如 python39.dll
。
在某些平台上,Python 将查找并载入名称中带有 abi3
标签的共享库文件 (例如 mymodule.abi3.so
)。 它不会检查这样的扩展是否兼容稳定 ABI。 使用方 (或其打包工具) 需要确保这一些,例如,基于 3.10+ 受限 API 编译的扩展不可被安装于更低版本的 Python 中。
稳定 ABI 中的所有函数都会作为 Python 的共享库中的函数存在,而不仅是作为宏。 这使得它们可以在不使用 C 预处理器的语言中使用。
受限 API 的作用域和性能¶
受限 API 的目标是允许使用在完整 C API 中可用的任何东西,但可能会有性能上的损失。
例如,虽然 PyList_GetItem()
是可用的,但其 “不安全的” 宏版本 PyList_GET_ITEM()
则是不可用的。 这个宏的运行速度更快因为它可以利用版本专属的列表对象实现细节。
在未定义 Py_LIMITED_API
的情况下,某些 C API 函数将由宏来执行内联或替换。 定义 Py_LIMITED_API
会禁用这样的内联,允许提升 Python 的数据结构稳定性,但有可能降低性能。
通过省略 Py_LIMITED_API
定义,可以使基于版本专属的 ABI 来编译受限 API 扩展成为可能。 这能提升其在相应 Python 版本上的性能,但也将限制其兼容性。 基于 Py_LIMITED_API
进行编译将产生一个可在版本专属扩展不可用的场合分发的扩展 – 例如,针对即将发布的 Python 版本的预发布包。
受限 API 警示¶
请注意使用 Py_LIMITED_API
进行编译 无法 完全保证代码能够兼容 受限 API 或 稳定 ABI。 Py_LIMITED_API
仅仅涵盖定义部分,但一个 API 还包括其他因素,如预期的语义等。
Py_LIMITED_API
不能处理的一个问题是附带在较低 Python 版本中无效的参数调用某个函数。 例如,考虑一个接受 NULL
作为参数的函数。 在 Python 3.9 中,NULL
现在会选择一个默认行为,但在 Python 3.8 中,该参数将被直接使用,导致一个 NULL
引用被崩溃。 类似的参数也适用于结构体的字段。
另一个问题是当定义了 Py_LIMITED_API
时某些结构体字段目前不会被隐藏,即使它们是受限 API 的一部分。
出于这些原因,我们建议用要支持的 所有 Python 小版本号来测试一个扩展,并最好是用其中 最低 的版本来编译它。
我们还建议查看所使用 API 的全部文档以检查其是否显式指明为受限 API 的一部分。 即使定义了 Py_LIMITED_API
,少数私有声明还是会出于技术原因(或者甚至是作为程序缺陷在无意中)被暴露出来。
还要注意受限 API 并不必然是稳定的:在 Python 3.8 上用 Py_LIMITED_API
编译扩展意味着该扩展能在 Python 3.12 上运行,但它将不一定能用 Python 3.12 编译。 特别地,在稳定 ABI 保持稳定的情况下,部分受限 API 可能会被弃用并被移除。
平台的考虑¶
ABI 的稳定性不仅取决于 Python,取决于所使用的编译器、低层级库和编译器选项等。 对于 稳定 ABI 的目标来说,这些细节定义了一个 “平台”。 它们通常会依赖于 OS 类型和处理器架构等。
确保在特定平台上的所有 Python 版本都以不破坏稳定 ABI 的方式构建是每个特定 Python 分发方的责任。 来自 python.org
以及许多第三方分发商的 Windows 和 macOS 发布版都必于这种情况。
受限 API 的内容¶
目前 受限 API 包括下面这些项:
PyBaseObject_Type
PyByteArrayIter_Type
PyBytesIter_Type
PyBytes_DecodeEscape()
PyBytes_Repr()
PyCFunction_GetFlags()
PyCFunction_GetFunction()
PyCFunction_GetSelf()
PyCFunction_Type
PyCapsule_Type
PyClassMethodDescr_Type
PyDictItems_Type
PyDictIterItem_Type
PyDictIterKey_Type
PyDictIterValue_Type
PyDictKeys_Type
PyDictProxy_Type
PyDictRevIterItem_Type
PyDictRevIterKey_Type
PyDictRevIterValue_Type
PyDictValues_Type
PyEllipsis_Type
PyEnum_Type
PyErr_Display()
PyErr_ProgramText()
PyExc_ArithmeticError
PyExc_AssertionError
PyExc_AttributeError
PyExc_BaseException
PyExc_BaseExceptionGroup
PyExc_BlockingIOError
PyExc_BrokenPipeError
PyExc_BufferError
PyExc_BytesWarning
PyExc_ChildProcessError
PyExc_ConnectionAbortedError
PyExc_ConnectionError
PyExc_ConnectionRefusedError
PyExc_ConnectionResetError
PyExc_DeprecationWarning
PyExc_EOFError
PyExc_EncodingWarning
PyExc_EnvironmentError
PyExc_Exception
PyExc_FileExistsError
PyExc_FileNotFoundError
PyExc_FloatingPointError
PyExc_FutureWarning
PyExc_GeneratorExit
PyExc_IOError
PyExc_ImportError
PyExc_ImportWarning
PyExc_IndentationError
PyExc_IndexError
PyExc_InterruptedError
PyExc_IsADirectoryError
PyExc_KeyError
PyExc_KeyboardInterrupt
PyExc_LookupError
PyExc_MemoryError
PyExc_ModuleNotFoundError
PyExc_NameError
PyExc_NotADirectoryError
PyExc_NotImplementedError
PyExc_OSError
PyExc_OverflowError
PyExc_PendingDeprecationWarning
PyExc_PermissionError
PyExc_ProcessLookupError
PyExc_RecursionError
PyExc_ReferenceError
PyExc_ResourceWarning
PyExc_RuntimeError
PyExc_RuntimeWarning
PyExc_StopAsyncIteration
PyExc_StopIteration
PyExc_SyntaxError
PyExc_SyntaxWarning
PyExc_SystemError
PyExc_SystemExit
PyExc_TabError
PyExc_TimeoutError
PyExc_TypeError
PyExc_UnboundLocalError
PyExc_UnicodeDecodeError
PyExc_UnicodeEncodeError
PyExc_UnicodeError
PyExc_UnicodeTranslateError
PyExc_UnicodeWarning
PyExc_UserWarning
PyExc_ValueError
PyExc_Warning
PyExc_WindowsError
PyExc_ZeroDivisionError
PyExceptionClass_Name()
PyFilter_Type
PyGILState_STATE
PyGetSetDescr_Type
PyListIter_Type
PyListRevIter_Type
PyLongRangeIter_Type
PyMap_Type
PyMemberDescr_Type
PyMemoryView_Type
PyMethodDescr_Type
PyModuleDef_Base
PyModuleDef_Type
PyOS_InterruptOccurred()
PyOS_mystricmp()
PyOS_mystrnicmp()
PyObject_DelItemString()
PyRangeIter_Type
PyRange_Type
PyReversed_Type
PySequence_In()
PySetIter_Type
PySuper_Type
PyThread_GetInfo()
PyThread_acquire_lock()
PyThread_acquire_lock_timed()
PyThread_allocate_lock()
PyThread_exit_thread()
PyThread_free_lock()
PyThread_get_stacksize()
PyThread_get_thread_ident()
PyThread_get_thread_native_id()
PyThread_init_thread()
PyThread_release_lock()
PyThread_set_stacksize()
PyThread_start_new_thread()
PyTraceBack_Here()
PyTraceBack_Print()
PyTraceBack_Type
PyTupleIter_Type
PyUnicodeIter_Type
PyUnicode_Append()
PyUnicode_AppendAndDel()
PyUnicode_AsDecodedObject()
PyUnicode_AsDecodedUnicode()
PyUnicode_AsEncodedObject()
PyUnicode_AsEncodedUnicode()
PyUnicode_BuildEncodingMap()
PyUnicode_DecodeCodePageStateful()
PyUnicode_FromOrdinal()
PyUnicode_GetDefaultEncoding()
PyUnicode_Partition()
PyUnicode_RPartition()
PyUnicode_RSplit()
PyUnicode_Resize()
PyVarObject.ob_base
PyWeakReference
PyWrapperDescr_Type
PyZip_Type
Py_FileSystemDefaultEncodeErrors
Py_FileSystemDefaultEncoding
Py_GetRecursionLimit()
Py_HasFileSystemDefaultEncoding
Py_MakePendingCalls()
Py_SetRecursionLimit()
Py_UTF8Mode
Py_intptr_t
Py_uintptr_t
ssizessizeargfunc
ssizessizeobjargproc
symtable