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 版本一起使用,并且可以使用直到该版本引入的受限 API。与其直接使用
PY_VERSION_HEX宏,不如硬编码最小次要版本(例如,对于 Python 3.10 为0x030A0000),以便在使用将来的 Python 版本编译时保持稳定性。您也可以将
Py_LIMITED_API定义为3。这与0x03020000(Python 3.2,引入有限 API 的版本)的效果相同。
稳定 ABI¶
为了实现这一点,Python 提供了一个稳定 ABI:一组在 Python 3.x 版本之间保持兼容的符号。
稳定 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 定义,可以将有限 API 扩展与特定版本的 ABI 编译在一起。这可以提高该 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 解引用和崩溃。类似的论点适用于结构体的字段。
另一个问题是,即使某些结构体字段是有限 API 的一部分,当定义了 Py_LIMITED_API 时,它们目前也不会被隐藏。
出于这些原因,我们建议使用它支持的所有次要 Python 版本测试扩展,最好使用最低版本进行构建。
我们还建议查看所有使用 API 的文档,以检查它是否明确属于有限 API 的一部分。即使定义了 Py_LIMITED_API,出于技术原因(甚至无意中,作为错误),也会公开一些私有声明。
还要注意,有限 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_TypePyByteArrayIter_TypePyBytesIter_TypePyBytes_DecodeEscape()PyBytes_Repr()PyCFunction_Call()PyCFunction_GetFlags()PyCFunction_GetFunction()PyCFunction_GetSelf()PyCFunction_TypePyCapsule_TypePyClassMethodDescr_TypePyDictItems_TypePyDictIterItem_TypePyDictIterKey_TypePyDictIterValue_TypePyDictKeys_TypePyDictProxy_TypePyDictRevIterItem_TypePyDictRevIterKey_TypePyDictRevIterValue_TypePyDictValues_TypePyEllipsis_TypePyEnum_TypePyErr_Display()PyErr_ProgramText()PyEval_CallFunction()PyEval_CallMethod()PyEval_CallObjectWithKeywords()PyExc_ArithmeticErrorPyExc_AssertionErrorPyExc_AttributeErrorPyExc_BaseExceptionPyExc_BaseExceptionGroupPyExc_BlockingIOErrorPyExc_BrokenPipeErrorPyExc_BufferErrorPyExc_BytesWarningPyExc_ChildProcessErrorPyExc_ConnectionAbortedErrorPyExc_ConnectionErrorPyExc_ConnectionRefusedErrorPyExc_ConnectionResetErrorPyExc_DeprecationWarningPyExc_EOFErrorPyExc_EncodingWarningPyExc_EnvironmentErrorPyExc_ExceptionPyExc_FileExistsErrorPyExc_FileNotFoundErrorPyExc_FloatingPointErrorPyExc_FutureWarningPyExc_GeneratorExitPyExc_IOErrorPyExc_ImportErrorPyExc_ImportWarningPyExc_IndentationErrorPyExc_IndexErrorPyExc_InterruptedErrorPyExc_IsADirectoryErrorPyExc_KeyErrorPyExc_KeyboardInterruptPyExc_LookupErrorPyExc_MemoryErrorPyExc_ModuleNotFoundErrorPyExc_NameErrorPyExc_NotADirectoryErrorPyExc_NotImplementedErrorPyExc_OSErrorPyExc_OverflowErrorPyExc_PendingDeprecationWarningPyExc_PermissionErrorPyExc_ProcessLookupErrorPyExc_RecursionErrorPyExc_ReferenceErrorPyExc_ResourceWarningPyExc_RuntimeErrorPyExc_RuntimeWarningPyExc_StopAsyncIterationPyExc_StopIterationPyExc_SyntaxErrorPyExc_SyntaxWarningPyExc_SystemErrorPyExc_SystemExitPyExc_TabErrorPyExc_TimeoutErrorPyExc_TypeErrorPyExc_UnboundLocalErrorPyExc_UnicodeDecodeErrorPyExc_UnicodeEncodeErrorPyExc_UnicodeErrorPyExc_UnicodeTranslateErrorPyExc_UnicodeWarningPyExc_UserWarningPyExc_ValueErrorPyExc_WarningPyExc_WindowsErrorPyExc_ZeroDivisionErrorPyExceptionClass_Name()PyFilter_TypePyGILState_STATEPyGetSetDescr_TypePyListIter_TypePyListRevIter_TypePyLongRangeIter_TypePyLong_GetInfo()PyMap_TypePyMemberDescr_TypePyMemoryView_TypePyMethodDescr_TypePyModuleDef_BasePyModuleDef_TypePyOS_InterruptOccurred()PyOS_mystricmp()PyOS_mystrnicmp()PyObject_DelItemString()PyObject_SelfIter()PyRangeIter_TypePyRange_TypePyReversed_TypePySequence_In()PySetIter_TypePySuper_TypePySys_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_TypePyTupleIter_TypePyUnicodeIter_TypePyUnicode_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_basePyWeakReferencePyWrapperDescr_TypePyZip_TypePy_FileSystemDefaultEncodeErrorsPy_FileSystemDefaultEncodingPy_GetRecursionLimit()Py_HasFileSystemDefaultEncodingPy_MakePendingCalls()Py_SetRecursionLimit()Py_UTF8ModePy_intptr_tPy_uintptr_tssizessizeargfuncssizessizeobjargprocsymtable