C API 稳定性¶
除非另有说明,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 开发并花费额外的精力来适应更改。
稳定的应用程序二进制接口¶
为简单起见,本文档讨论的是*扩展*,但有限的 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。为了在将来使用 Python 版本进行编译时保持稳定性,不要直接使用
PY_VERSION_HEX
宏,而是硬编码一个最小的小版本(例如,Python 3.10 的0x030A0000
)。您还可以将
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
,出于技术原因(甚至可能是意外的 bug),也会公开一些私有声明。
另请注意,有限的 API 不一定稳定:使用 Python 3.8 中的 Py_LIMITED_API
进行编译意味着扩展将与 Python 3.12 一起运行,但不一定会使用 Python 3.12 进行*编译*。特别是,有限 API 的某些部分可能会被弃用和删除,前提是稳定的 ABI 保持稳定。
平台注意事项¶
ABI 稳定性不仅取决于 Python,还取决于所使用的编译器、底层库和编译器选项。 对于稳定 ABI,这些细节定义了一个“平台”。 它们通常取决于操作系统类型和处理器架构。
每个特定的 Python 发行商都有责任确保特定平台上的所有 Python 版本都以不破坏稳定 ABI 的方式构建。 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