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

Python 增强提案

PEP 652 – 维护稳定 ABI

作者:
Petr Viktorin <encukou at gmail.com>
讨论地址:
Discourse 帖子
状态:
最终版
类型:
标准跟踪
创建时间:
2021 年 2 月 9 日
Python 版本:
3.10
决议:
Python-Dev 邮件

目录

重要事项

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

×

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

摘要

CPython 的有限 C-API 和稳定 ABI(在 PEP 384 中引入)将被正式定义在一个单独的确定性文件中,并进行测试和记录。

动机

PEP 384 定义了有限 API 和稳定 ABI,允许 CPython 扩展程序和嵌入程序编译与任何后续 3.x 版本二进制兼容的扩展模块。从理论上讲,这带来了几个优势

  • 每个平台只需要构建一次模块即可支持多个 Python 版本,从而减少构建所需的 时间、资源和维护人员的精力(以可能更差的性能为代价)。
  • 使用稳定 ABI 的二进制轮子可以在整个预发布期间与新版本的 CPython 配合使用,并且可以在无法从源代码构建的环境中进行测试。
  • 有限 API 隐藏实现细节带来的一个受欢迎的副作用是,它正在成为替代 Python 实现的可行目标,这些实现将与完整的 C API 不兼容。

然而,事后看来,PEP 384 及其实现存在几个问题

  • 它定义得不好。根据 PEP 384,函数采用的是选择退出机制:所有没有特别标记的函数都属于稳定 ABI。在实践中,Windows 存在一个选择加入列表。对于用户来说,有一个 #define 应该只提供稳定 ABI,但没有确保其保持更新的流程。也没有更新文档的流程。
  • 直到最近,稳定 ABI 都没有经过任何测试。它往往会崩溃。例如,将函数更改为宏可能会破坏稳定 ABI,因为函数符号会被删除。
  • 无法弃用有限 API 的部分。
  • 它不完整。一些操作在稳定 ABI 中不可用,除了“我们忘记了”之外没有其他原因。(不过,这一点是 PEP 无法解决的。)

本 PEP 更明确地定义了有限 API,并引入了旨在使稳定 ABI 和有限 API 更有用、更健壮的流程。

基本原理

本 PEP 包含大量澄清和定义,但只有一个重大技术变更:稳定 ABI 将在一个人工维护的“清单”文件中显式列出。

人们一直在努力自动收集这些列表,例如,通过扫描从 Python 导出的符号。这种自动化似乎比手工制作的文件更容易维护,但存在重大问题:例如,导出的符号集在不同平台上有所不同。此外,更新显式清单的成本与更改需要永远支持的 API 所需的整体工作量相比微不足道(或者,如果 Python 3 提前结束生命周期,则直到 Python 3 结束生命周期为止)。

本 PEP 建议清单中自动生成一些内容:最初是文档和 DLL 内容,之后可能会自动执行测试。

稳定 ABI 与有限 API

PEP 384 和本文件处理有限 API稳定 ABI,这两个概念相关但不同。简而言之

  • 稳定 ABI 是一个承诺,即使用 CPython 3.x 编译的某些扩展程序将与所有后续版本的 CPython 3.x 二进制兼容。
  • 有限 API 是 CPython C API 的一个子集,它生成这样的扩展程序。

本节将阐明这些术语,并定义一些语义(无论是预先存在的还是在此新提出的)。

术语“扩展程序”用作使用 Python API 的所有代码的简写,例如扩展模块或嵌入 Python 的软件。

稳定 ABI

CPython 的稳定 ABI 是一个承诺,即针对特定稳定 ABI 版本构建的扩展程序将与任何相同主要版本的更新解释器兼容。

稳定 ABI 不会定义一个完整的二进制接口:内存中结构的布局或函数调用约定等重要细节由平台以及编译器及其设置决定。稳定 ABI 的承诺仅在这些底层细节也保持稳定的情况下适用。

例如,使用 CPython 3.10 稳定 ABI 构建的扩展程序可以在 CPython 3.11、3.12 等中使用。它不一定与 CPython 4.0 兼容,也不一定与不同平台上的 CPython 3.10 兼容。

稳定 ABI 通常不向前兼容:使用 CPython 3.10 构建和测试的扩展程序通常不与 CPython 3.9 兼容。

注意

例如,从 Python 3.10 开始,Py_tp_doc 插槽可以设置为 NULL,而在旧版本中,NULL 值很可能会导致解释器崩溃。

稳定 ABI 以性能换取稳定性。例如,针对特定 CPython 版本构建的扩展程序将自动使用稳定 ABI 中更快的宏而不是函数。

未来的 Python 版本可能会弃用稳定 ABI 的某些成员。弃用的成员仍然可以使用,但可能会出现性能下降或内存/资源泄漏等问题(在最极端情况下)。

有限 API

稳定 ABI 的承诺适用于从限制自身于有限 API(应用程序编程接口)的代码编译的扩展程序。有限 API 是 CPython C API 的一个子集。

针对有限 API 的扩展程序应该定义预处理宏 Py_LIMITED_API3 或当前的 PYTHON_API_VERSION。这将启用几个函数的稳定 ABI 版本,并将定义限制在有限 API 范围内。(但是,请注意,宏并不完美:由于技术问题或疏忽,即使定义了宏,也可能暴露一些非有限 API。)

有限 API 不保证稳定。将来,有限 API 的部分可能会被弃用。它们甚至可能被删除,只要稳定 ABI 保持稳定,并且遵循 Python 的一般向后兼容性策略 PEP 387

注意

例如,函数声明可能会从公共头文件中删除,但保留在库中。这目前是未来的可能性;本 PEP 并未提出弃用和删除的具体流程。

有限 API 的目标是涵盖与解释器交互所需的一切。不将公共 API 包含在有限子集中的主要原因应该是它需要 CPython 版本之间发生变化的实现细节(如结构内存布局)——通常是为了提高性能。

有限 API 不限于 CPython。其他实现鼓励实现它并帮助推动它的设计。

规范

为了使稳定 ABI 更有用、更健壮,建议进行以下更改。

稳定 ABI 清单

稳定 ABI 的所有成员——函数、类型定义、结构、数据、宏和常量——将显式列在一个单独的“清单”文件中,Misc/stable_abi.txt

对于结构,将显式列出稳定 ABI 用户可以访问的任何字段。

清单还将用作有限 API 的确定性列表。不是有限 API 的成员,但属于稳定 ABI 的成员(例如,PyObject.ob_type,可以通过 Py_TYPE 宏访问),将被注释为这种类型。

对于仅在某些系统上可用的项目,清单将记录确定其存在的特性宏(例如,MS_WINDOWSHAVE_FORK)。为了使实现(以及从非 C 语言的使用)更容易,所有此类宏都将使用简单名称。如果未来的项目需要“否定”宏或复杂表达式(例如,假设的 #ifndef MACOSX#if defined(POSIX) && !defined(LINUX)),将派生一个新的特性宏。

清单的格式将在需要时更改。它应该只被 CPython 存储库中的脚本使用。如果需要稳定的列表,可以添加一个脚本生成它。

以下内容将从 ABI 清单中生成

  • Windows 共享库的源代码,PC/python3dll.c
  • 文档的输入(见下文)。
  • 测试用例,用于检查符号的运行时可用性(见下文)。

以下内容将在持续集成中针对稳定 ABI 清单进行检查

  • 引用计数摘要,Doc/data/refcounts.txt,包括稳定 ABI 中的所有函数(以及其他)。
  • Python.h 中包含并设置 Py_LIMITED_API 后声明的函数/结构以及定义的常量/宏。(最初仅限 Linux;将来可能会在其他系统上添加检查。)

在初始实现之后,将添加函数参数等详细信息,并检查清单的内部一致性(例如,函数签名中使用的所有类型都是 API 的一部分)。

稳定 ABI 内容

初始稳定 ABI 清单将包括

  • PEP 384 中指定的稳定 ABI。
  • PC/python3dll.c 中列出的所有内容。
  • 这些函数返回或作为参数接受的所有结构(结构类型定义)。(此类结构的字段不会被添加。)
  • 新的插槽类型,例如 Py_am_aiter
  • 类型标记 Py_TPFLAGS_DEFAULTPy_TPFLAGS_BASETYPEPy_TPFLAGS_HAVE_GCPy_TPFLAGS_METHOD_DESCRIPTOR
  • 调用约定 METH_*(除了已弃用的约定)。
  • 宏所需的所有 API 都是稳定 ABI(标注为不属于有限 API 的一部分)。

当此 PEP 被接受时,不再存在于 CPython 中的项目将从列表中删除。

根据以下清单,可以将其他项目添加到初始清单中。

记录有限 API

带有“有限 API 的一部分”的注释将自动添加到 Python 的文档中,与返回借用引用的函数上的注释类似。

有限 API 的所有成员的完整列表也将添加到文档中。

测试稳定 ABI

将添加一个自动生成的测试模块,以确保稳定 ABI 中包含的所有符号在编译时可用。

更改有限 API

将添加一个用于更改有限 API 的清单,包括向其中添加新项目和删除现有项目,并将添加到 Devguide 中。清单将 1) 提到 Python C API 设计中的最佳实践和常见陷阱,以及 2) 在更改有限 API 时,指导开发人员了解需要更改的文件和需要运行的脚本。

以下是清单的初始建议。(PEP 被接受后,请参阅 Devguide 以获取当前版本。)

请注意,此清单适用于新更改;现有有限 API 中的几个项目被视为祖先项目,今天无法添加。

设计注意事项

  • 确保更改不会破坏自 3.5 版以来的任何 Python 版本的稳定 ABI。
  • 确保没有公开的名称是私有的(即以下划线开头)。
  • 确保新 API 有良好的文档记录。
  • 确保添加的函数的所有参数和返回值类型以及添加的结构的所有字段类型都是有限 API(或标准 C)的一部分。
  • 确保新 API 及其预期用途遵循标准 C,而不仅仅是当前支持平台的功能。具体来说,请遵循 PEP 7 中指定的 C 方言。
    • 不要将函数指针强制转换为 void*(数据指针)或反之。
  • 确保新 API 遵循引用计数约定。(遵循这些约定使得 API 更容易推理,也更容易在其他 Python 实现中使用。)
    • 不要从函数返回借用引用。
    • 不要窃取对函数参数的引用。
  • 确保所有适用结构字段、参数和返回值的所有权规则和生存期都定义明确。
  • 考虑用户易用性。(在 C 中,易用性本身并不重要;真正有用的是减少使用 API 所需的样板代码。错误喜欢隐藏在样板代码中。)
    • 如果一个函数将经常使用特定参数值调用,请考虑将其设为默认值(当传入 NULL 时使用)。
  • 考虑未来的扩展:例如,如果未来版本的 Python 需要向你的结构中添加一个新字段,那么该如何完成?
  • 对可能在未来的 CPython 版本中发生更改或在 C API 实现之间不同的细节做出尽可能少的假设
    • GIL
    • 垃圾回收
    • PyObject、列表/元组和其他结构的内存布局

如果遵循这些准则会影响性能,请在非有限 API 中添加一个快速函数(或宏),并在有限 API 中添加一个稳定的等效函数。

如果有任何不清楚的地方,或者你有充分的理由违反这些准则,请考虑在 capi-sig 邮件列表中讨论更改。

程序

  • 将声明移动到 Include/ 下的直接头文件中,进入 #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03yy0000 块中(其中 yy 对应于目标 CPython 版本)。
  • 在稳定 ABI 清单 Misc/stable_abi.txt 中创建一个条目。
  • 使用 make regen-all 重新生成自动生成的代码。(或非 make 平台的替代方法)
  • 使用 make check-abi 构建 Python 并运行检查。(或非 make 平台的替代方法)

针对扩展程序和嵌入程序的建议

以下注释将添加到文档中,以及有关此主题的更多信息,以及我们提供的保证

扩展作者应该在他们支持的所有 Python 版本上进行测试,并且最好使用最低版本进行构建。

定义 Py_LIMITED_API 进行编译不是保证你的代码符合有限 API 或稳定 ABI。Py_LIMITED_API 仅涵盖定义,但 API 还包括其他问题,例如预期的语义。

以下是一些 Py_LIMITED_API 不保护的示例

  • 使用无效参数调用函数
  • 从 Python 3.9 开始接受 NULL 参数值的函数将在 Python 3.8 下传递 NULL 时失败。只有使用 3.8(或更低版本)进行测试才能发现这个问题。
  • 一些结构包含一些属于稳定 ABI 的字段,以及其他不属于的字段。Py_LIMITED_API 不会过滤掉这样的“私有”字段。
  • 使用未记录为稳定 ABI 的一部分但即使定义了 Py_LIMITED_API 仍然公开的代码,将来可能会出现问题。尽管团队尽了最大努力,但这类问题还是可能会发生。

针对 Python 再发行者的说明

稳定 ABI 的承诺依赖于稳定的底层 ABI 细节,例如结构在内存中的布局和函数调用约定,这些细节受编译器及其设置的影响。为了保持承诺,这些细节在特定平台上的 CPython 3.x 版本之间不能发生变化。

向后兼容性

向后兼容性是一个很棒的想法!

此 PEP 旨在与现有的稳定 ABI 和有限 API 完全兼容,但更明确地定义了这些术语。它可能与对现有稳定 ABI/有限 API 的某些解释不一致。

安全隐患

无。

如何教授此内容

技术文档将提供在 Doc/c-api/stable 中,并从“What’s New”文档中链接。CPython 核心开发人员的文档将添加到 devguide 中。

参考实现

问题 43795

未来展望

以下问题不在此 PEP 的范围之内,但展示了可能的未来方向。

定义弃用/删除流程

虽然此 PEP 承认有限 API 的某些部分可能在将来被弃用或删除,但执行此操作的过程不在范围之内,并留给可能将来出现的 PEP。

ABI 清单的 C 语法

将 ABI 清单作为 C 头文件或从清单生成头文件可能很有用。同样,两者都是未来的选择。

未解决的问题

目前还没有。


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

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