Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 384 – 定义稳定的 ABI

作者:
Martin von Löwis <martin at v.loewis.de>
状态:
最终
类型:
标准跟踪
创建:
2009年5月17日
Python 版本:
3.2
历史记录:


目录

重要

此 PEP 是一个历史文档。最新的规范文档现在可以在 C API 稳定性(用户文档)和 更改 Python 的 C API(开发人员文档)中找到。

×

请参阅 PEP 1,了解如何提出更改。

摘要

目前,每个功能版本都会为 Windows 上的 Python DLL 引入一个新的名称,并且可能会导致 Unix 上的扩展模块出现不兼容性。此 PEP 提出定义一组稳定的 API 函数,这些函数保证在 Python 3 的整个生命周期中可用,并且在版本之间也保持二进制兼容性。扩展模块和嵌入 Python 的应用程序只要限制在使用这个稳定的 ABI,就可以与不同的功能版本一起工作。

基本原理

ABI 不兼容的主要来源是内存中结构布局的更改。例如,字符串驻留的工作方式或用于表示对象大小的数据类型在 Python 2.x 的生命周期中发生了变化。因此,如果扩展模块直接访问字符串、列表或元组的字段,并且其代码加载到较新版本的解释器中而没有重新编译,则会发生错误:其他字段的偏移量可能已更改,导致扩展模块访问错误的数据。

在某些情况下,不兼容性只会影响解释器的内部对象,例如帧或代码对象。例如,行号的表示方式在 2.x 的生命周期中发生了变化,局部变量的存储方式也发生了变化(由于引入了闭包)。即使大多数应用程序可能从未使用过这些对象,更改它们也需要更改 PYTHON_API_VERSION。

在 Linux 上,对 ABI 的更改通常不是什么大问题:系统将提供默认的 Python 安装,并且许多扩展模块已经为该版本预编译提供。如果需要其他模块或其他 Python 版本,用户通常可以在系统上自行编译它们,从而生成使用正确 ABI 的模块。

在 Windows 上,不同 Python 版本的同时安装很常见,扩展模块由其作者编译,而不是由最终用户编译。为了降低 ABI 不兼容性的风险,Python 目前为每个功能版本引入了一个新的 DLL 名称 pythonXY.dll,无论 ABI 不兼容性是否实际存在。

使用此 PEP,可以减少二进制扩展模块对特定 Python 功能版本的依赖性,并且可以使嵌入 Python 的应用程序能够与不同的版本一起工作。

规范

ABI 规范分为两个部分:API 规范,指定哪些函数(组)可用于 ABI;以及链接规范,指定要链接哪些库。实际的 ABI(内存中结构的布局、函数调用约定)未指定,但由编译器隐含。作为建议,建议为选定的平台推荐特定的 ABI。

在 Python 演进过程中,将添加新的 ABI 函数。使用它们的应用程序将对 Python 的最低版本有要求;此 PEP 没有提供任何机制供此类应用程序在 Python 库过旧时回退。

术语

想要使用此 ABI 的应用程序和扩展模块从现在开始统称为“应用程序”。

头文件和预处理器定义

应用程序只能包含头文件 Python.h(在包含任何系统头文件之前),或者可选地包含 pyconfig.h,然后是 Python.h。

在应用程序的编译过程中,必须定义预处理器宏 Py_LIMITED_API。这样做会隐藏所有不属于 ABI 的定义。

结构体

只有以下结构体和结构体字段可供应用程序访问

  • PyObject (ob_refcnt, ob_type)
  • PyVarObject (ob_base, ob_size)
  • PyMethodDef (ml_name, ml_meth, ml_flags, ml_doc)
  • PyMemberDef (name, type, offset, flags, doc)
  • PyGetSetDef (name, get, set, doc, closure)
  • PyModuleDefBase (ob_base, m_init, m_index, m_copy)
  • PyModuleDef (m_base, m_name, m_doc, m_size, m_methods, m_traverse, m_clear, m_free)
  • PyStructSequence_Field (name, doc)
  • PyStructSequence_Desc (name, doc, fields, sequence)
  • PyType_Slot (见下文)
  • PyType_Spec (见下文)

这些字段的访问器宏(Py_REFCNT、Py_TYPE、Py_SIZE)也可供应用程序使用。

以下类型可用,但是不透明的(即不完整的)

  • PyThreadState
  • PyInterpreterState
  • struct _frame
  • struct symtable
  • struct _node
  • PyWeakReference
  • PyLongObject
  • PyTypeObject

类型对象

类型对象的结构不可用于应用程序;不再允许声明“静态”类型对象(对于使用此 ABI 的应用程序)。相反,类型对象是动态创建的。为了便于创建类型(特别是能够轻松填写函数指针),以下结构体和函数可用

typedef struct{
  int slot;    /* slot id, see below */
  void *pfunc; /* function pointer */
} PyType_Slot;

typedef struct{
  const char* name;
  int basicsize;
  int itemsize;
  unsigned int flags;
  PyType_Slot *slots; /* terminated by slot==0. */
} PyType_Spec;

PyObject* PyType_FromSpec(PyType_Spec*);

要指定一个插槽,必须提供一个唯一的插槽 ID。新的 Python 版本可能会引入新的插槽 ID,但插槽 ID 永远不会被回收。插槽可能会被弃用,但在整个 Python 3.x 中继续受支持。

插槽 ID 的命名方式与 Python 3.1 中保存指针的结构体的字段名称相同,并在前面添加了 Py_ 前缀(即 Py_tp_dealloc 而不是仅 tp_dealloc)

  • tp_dealloc, tp_getattr, tp_setattr, tp_repr, tp_hash, tp_call, tp_str, tp_getattro, tp_setattro, tp_doc, tp_traverse, tp_clear, tp_richcompare, tp_iter, tp_iternext, tp_methods, tp_base, tp_descr_get, tp_descr_set, tp_init, tp_alloc, tp_new, tp_is_gc, tp_bases, tp_del
  • nb_add nb_subtract nb_multiply nb_remainder nb_divmod nb_power nb_negative nb_positive nb_absolute nb_bool nb_invert nb_lshift nb_rshift nb_and nb_xor nb_or nb_int nb_float nb_inplace_add nb_inplace_subtract nb_inplace_multiply nb_inplace_remainder nb_inplace_power nb_inplace_lshift nb_inplace_rshift nb_inplace_and nb_inplace_xor nb_inplace_or nb_floor_divide nb_true_divide nb_inplace_floor_divide nb_inplace_true_divide nb_index
  • sq_length sq_concat sq_repeat sq_item sq_ass_item sq_contains sq_inplace_concat sq_inplace_repeat
  • mp_length mp_subscript mp_ass_subscript

在类型定义期间无法设置以下字段: - tp_dict tp_mro tp_cache tp_subclasses tp_weaklist tp_print - tp_weaklistoffset tp_dictoffset

typedefs

除了上面列出的结构体的 typedefs 之外,以下 typedefs 也可用。它们包含在 ABI 中意味着底层类型不得在平台上更改(即使它可能跨平台不同)。

  • Py_uintptr_t Py_intptr_t Py_ssize_t
  • unaryfunc binaryfunc ternaryfunc inquiry lenfunc ssizeargfunc ssizessizeargfunc ssizeobjargproc ssizessizeobjargproc objobjargproc objobjproc visitproc traverseproc destructor getattrfunc getattrofunc setattrfunc setattrofunc reprfunc hashfunc richcmpfunc getiterfunc iternextfunc descrgetfunc descrsetfunc initproc newfunc allocfunc
  • PyCFunction PyCFunctionWithKeywords PyNoArgsFunction PyCapsule_Destructor
  • getter setter
  • PyOS_sighandler_t
  • PyGILState_STATE
  • Py_UCS4

最值得注意的是,Py_UNICODE 不可用作 typedef,因为同一 Python 版本可能在同一平台上使用不同的定义(取决于它是否使用窄或宽代码单元)。需要访问 Unicode 字符串内容的应用程序可以将其转换为 wchar_t。

函数和类似函数的宏

默认情况下,所有函数都可用,除非下面排除。函数是否已记录无关紧要。

类似函数的宏(特别是字段访问宏)仍然可供应用程序使用,但会被函数调用替换(除非它们的定义仅引用 ABI 的功能,例如各种 _Check 宏)

ABI 函数声明不会更改其参数或返回类型。如果需要更改签名,将引入新函数。如果新函数是源代码兼容的(例如,如果仅返回类型更改),则可能会添加一个别名宏,以便在重新编译应用程序时将调用重定向到新函数。

如果无法继续提供旧函数,则可能会将其弃用,然后删除,导致使用该函数的应用程序出现错误。

排除的函数

所有以 _Py 开头的函数都不可用于应用程序。此外,所有期望应用程序不可用的参数类型的函数都从 ABI 中排除,例如 PyAST_FromNode(它期望一个 node*)。

以下头文件中声明的函数不是 ABI 的一部分

  • bytes_methods.h
  • cellobject.h
  • classobject.h
  • code.h
  • compile.h
  • datetime.h
  • dtoa.h
  • frameobject.h
  • funcobject.h
  • genobject.h
  • longintrepr.h
  • parsetok.h
  • pyarena.h
  • pyatomic.h
  • pyctype.h
  • pydebug.h
  • pytime.h
  • symtable.h
  • token.h
  • ucnhash.h

此外,期望 FILE* 的函数不是 ABI 的一部分,以避免依赖于 Windows 上 Microsoft C 运行时 DLL 的特定版本。

模块和类型初始化器和终结器函数不可用(PyByteArray_Init、PyOS_FiniInterrupts 和所有以 _Fini 或 _ClearFreeList 结尾的函数)。

一些处理解释器实现细节的函数不可用

  • PyInterpreterState_Head, PyInterpreterState_Next, PyInterpreterState_ThreadHead, PyThreadState_Next
  • Py_SubversionRevision, Py_SubversionShortBranch

PyStructSequence_InitType 不可用,因为它要求调用者提供一个静态类型对象。

Py_FatalError 将从 pydebug.h 移动到其他头文件(例如 pyerrors.h)。

可用函数的确切列表在 python3.dll 的 Windows 模块定义文件中给出 [1]

全局变量

表示类型和异常的全局变量可供应用程序使用。此外,宏中引用的选定全局变量(例如 Py_True 和 Py_False)可用。

python3.def 文件中给出了全局变量定义的完整列表 [1];声明为 DATA 的表示变量。

其他宏

所有定义符号常量的宏都可供应用程序使用;数值不会改变。

此外,以下宏可用

  • Py_BEGIN_ALLOW_THREADS, Py_BLOCK_THREADS, Py_UNBLOCK_THREADS, Py_END_ALLOW_THREADS

缓冲区接口

缓冲区接口(Py_buffer 类型、bf_getbuffer 和 bf_releasebuffer 类型插槽等)已从 ABI 中省略,因为目前尚不清楚 Py_buffer 结构的稳定性。可以在将来的版本中考虑将其包含在 ABI 中。

签名更改

许多函数目前都期望一个特定的结构体,即使调用者通常可以使用 PyObject*。这些函数已更改为期望 PyObject* 作为参数;这将导致在当前显式转换为参数类型的应用程序中发出警告。这些函数包括 PySlice_GetIndices、PySlice_GetIndicesEx、PyUnicode_AsWideChar 和 PyEval_EvalCode。

链接

在 Windows 上,应用程序应链接到 python3.dll;将提供一个导入库 python3.lib。此 DLL 将通过 /export 链接器选项将所有 API 函数重定向到完整的解释器 DLL,即 python3y.dll。

在 Unix 系统上,ABI 通常由 python 可执行文件本身提供。如果扩展模块使用 Py_LIMITED_API 编译,则 PyModule_Create 会更改为将 3 作为 API 版本传递;API 版本的版本检查将接受 3 或当前的 PYTHON_API_VERSION 作为符合要求的版本。如果 Python 编译为共享库,则它将同时安装为 libpython3.so 和 libpython3.y.so;符合此 PEP 的应用程序应链接到前者(扩展模块可以继续链接到没有 libpython 共享对象的库,而是依赖于运行时链接)。ABI 版本在符号上可用,名为 PYTHON_ABI_VERSION

同样在 Unix 上,PEP 3149 标签 abi<PYTHON_ABI_VERSION> 在扩展模块的文件名中被接受。不会检查以这种方式命名的文件是否实际上仅限于受限 API,并且由于 distutils 代码冻结,不会添加对构建此类文件的支持。

实现策略

此 PEP 将在一个分支 [2] 中实现,允许用户检查其模块是否符合 ABI。为了避免用户不得不重写其类型定义,将提供一个用于转换包含类型定义的 C 源代码的脚本 [3]

参考文献


来源:https://github.com/python/peps/blob/main/peps/pep-0384.rst

上次修改时间:2023-09-09 17:39:29 GMT