C API 的稳定性¶
Python 的 C 语言 API 包含于向下兼容政策 PEP 387 中。C API 会跟随小版本的发布而发生变化(比如 3.9 到 3.10 的时候),不过大多数变化都是源代码级兼容的,通常只会增加新的 API。已有 API 的修改或删除,只有在废止期过后或修复严重问题时才会进行。
CPython 的应用二进制接口(ABI)可以跨小版本实现前后兼容(只要以同样方式编译;参见下面的 平台的考虑 )。因此,用 Python 3.10.0 编译的代码可以在 3.10.8 上运行,反之亦然,但针对 3.9.x 和 3.10.x 则需分别进行编译。
带下划线前缀的是私有 API,如 _Py_InternalState
,即便是补丁发布版本中也可能不加通知地进行改动。
应用程序二进制接口的稳定版¶
Python 3.2 引入了 受限 API,Python 的 C API 的一个子集。 只使用受限 API 的扩展可以被一次性编译而适用于多个 Python 版本。 受限 API 的内容 如下所示。
为了实现这一点,Python 提供了一个 稳定 ABI: 一个将在各 Python 3.x 版本中保持兼容性的符号集合。 稳定 ABI 包含了在受限 API 中暴露的符号,但还包含其他符号 – 例如,支持旧版受限 API 所需的函数。
(简单起见,本文档只讨论了 扩展,但受限 API 和稳定 ABI 对于 API 的所有用法都同样适用 – 例如,嵌入 Python 等。)
-
Py_LIMITED_API
¶ 请在包括
Python.h
之前定义这个宏以选择只使用受限 API,并选择受限 API 的版本。将
Py_LIMITED_API
定义为对应你的扩展所支持的最低 Python 版本的PY_VERSION_HEX
的值。 扩展无需重编译即可适用于从指定版本开始的所有 Python 3 发布版,并可使用到该版本为止所引入的受限 API。不直接使用
PY_VERSION_HEX
宏,而是碍编码一个最小的次要版本(例如0x030A0000
表示 Python 3.10)以便在使用未来的 Python 版本进行编译时保持稳定。你还可以将
Py_LIMITED_API
定义为3
。 其效果与0x03020000
相同(即 Python 3.2,引入受限 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
PyBool_Type
PyByteArrayIter_Type
PyBytesIter_Type
PyBytes_DecodeEscape()
PyBytes_Repr()
PyCFunction_Call()
PyCFunction_GetFlags()
PyCFunction_GetFunction()
PyCFunction_GetSelf()
PyCFunction_New()
PyCFunction_NewEx()
PyCFunction_Type
PyCMethod_New()
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()
PyEval_CallFunction()
PyEval_CallMethod()
PyEval_CallObjectWithKeywords()
PyExc_ArithmeticError
PyExc_AssertionError
PyExc_AttributeError
PyExc_BaseException
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
PyLong_GetInfo()
PyMap_Type
PyMemberDescr_Type
PyMemoryView_Type
PyMethodDescr_Type
PyModuleDef_Base
PyModuleDef_Type
PyOS_InterruptOccurred()
PyOS_mystricmp()
PyOS_mystrnicmp()
PyOS_sighandler_t
PyOS_strtol()
PyOS_strtoul()
PyObject_DelItemString()
PyObject_SelfIter()
PyRangeIter_Type
PyRange_Type
PyReversed_Type
PySequence_In()
PySetIter_Type
PySuper_Type
PySys_HasWarnOptions()
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_InternImmortal()
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
getter
setter
ssizessizeargfunc
ssizessizeobjargproc
symtable