PEP 689 – 不稳定的 C API 层级
- 作者:
- Petr Viktorin <encukou at gmail.com>
- 讨论列表:
- Discourse 讨论串
- 状态:
- 最终
- 类型:
- 标准规范跟踪
- 依赖:
- 523
- 创建:
- 2022年4月22日
- Python 版本:
- 3.12
- 历史记录:
- 2022年4月27日, 2022年8月25日, 2022年10月27日
- 决议:
- Discourse 消息
摘要
C-API 的某些函数和类型被指定为不稳定,这意味着它们在补丁(错误修复/安全)版本中不会更改,但可能在次要版本之间(例如,在 3.11 和 3.12 之间)更改,而无需弃用警告。
任何以开头下划线命名的 C API 都被指定为内部,这意味着它可能会在没有任何通知的情况下更改或消失。
动机与理由
不稳定的 C API 层级
Python C-API 当前分为 三个稳定性层级
- 有限的 API,具有很高的兼容性期望
- 公共 API,遵循 向后兼容性策略,并且在更改之前需要弃用警告
- 内部(私有)API,可以随时更改。
需要访问 CPython 内部功能的工具(例如高级调试器和 JIT 编译器)通常针对 CPython 的次要系列版本构建,并假设所使用的 C-API 内部功能在补丁版本中不会更改。为了支持这些工具,我们需要在公共和私有 C-API 之间添加一个层级,并保证在整个次要系列版本中的稳定性:提议的不稳定层级。
某些函数,例如 PyCode_New()
,在文档中被标记为不稳定(“直接调用它可能会将您绑定到特定的 Python 版本”),并且在实践中也经常发生变化。不稳定层级应该使它们的状态即使对于那些没有仔细阅读文档的人来说也显而易见,从而使它们难以意外使用。
为私有 API 保留前导下划线
目前,CPython 开发人员对 API 名称中前导下划线的准确含义没有达成一致。它被用来表示两种不同的含义
- 可能在次要版本之间发生更改的 API,如这里提出的不稳定层级(例如,在 PEP 523 中引入的函数)。
- 私有 API,不应在 CPython 外部使用(例如,因为它可能会在没有任何通知的情况下更改,或者它依赖于非 CPython 代码无法保证的未记录的假设)。
含义不明确使得下划线不如它可能的那样有用。如果它仅标记私有 API,则 CPython 开发人员可以更改带下划线的函数或删除未使用的函数,而无需研究它们在 CPython 外部的文档记录或使用方式。
随着专用不稳定层级的引入,我们可以阐明前导下划线的含义。它应该只标记私有 API。
避免不必要地破坏代码
此 PEP 指定不稳定层级中的 API 应该具有特殊的名称前缀。这意味着函数(宏等)需要重命名。重命名后,旧名称应继续可用,直到进行不兼容的更改(即,直到调用站点需要更新)。换句话说,仅仅更改函数的层级不应该破坏用户的代码。
规范
C API 根据稳定性预期分为 三个“部分”(内部、公共和有限)。我们现在将它们称为稳定性层级,或简称层级。
将添加一个不稳定层级。
此层级中的 API(函数、类型等)将使用 PyUnstable_
前缀命名,且无前导下划线。
它们将在用于公共 API 的头文件中声明(Include/*.h
,而不是像 Include/unstable/
这样的子目录中)。
将引入几条处理不稳定层级的规则
- 不稳定 API 在补丁版本中不应有任何向后不兼容的更改,但可能会在次要版本(3.x.0,包括 3.x.0 的 Alpha 和 Beta 版本)中更改或删除。此类更改必须记录在案,并在“新增功能”文档中提及。
- 应进行向后不兼容的更改,以便使用它们的代码需要更新才能与新版本编译(例如,应添加/删除参数,或重命名函数,但参数的语义含义不应更改)。
- 不稳定 API 应记录在案并进行测试。
- 要将 API 从公共层级移动到不稳定层级,应在新的
PyUnstable_*
名称下公开它。旧名称应被弃用(例如,使用
Py_DEPRECATED
),但应继续可用,直到对 API 进行不兼容的更改。根据 Python 的向后兼容性策略(PEP 387),此弃用需要持续至少两个版本(没有 SC 异常)。但它也可以无限期持续——例如,如果 PEP 590 的 “临时”_PyObject_Vectorcall
是今天添加的,它将最初命名为PyUnstable_Object_Vectorcall
,并且没有计划删除此名称。在以下情况下,允许进行不兼容的更改(以及因此删除弃用的名称)而无需 SC 异常,就像该函数已经是 Unstable 层级的一部分一样
- 在 Python 3.12 之前引入的任何已记录为比默认值稳定性低的 API。
- 在 Python 3.12 之前引入的任何以前导下划线命名的 API。
有关示例,请参阅此 PEP 中指定的 初始不稳定 API。
- 要将内部 API 移动到不稳定层级,应在新的
PyUnstable_*
名称下公开它。如果旧名称已记录在案或在外部广泛使用,则应继续可用,直到进行不兼容的更改(并且调用站点需要更新)。它应该开始引发弃用警告(例如,使用
Py_DEPRECATED
)。 - 要将 API 从不稳定层级移动到公共层级,应在没有
PyUnstable_*
前缀的情况下公开它。旧名称应继续可用,直到 API 被弃用或删除。
- 即使在 Beta 功能冻结后,也允许为现有功能添加新的不稳定 API,直到第一个候选版本发布。核心开发人员需要在 Beta 期间达成共识。
这些规则将在 开发指南 中记录,并且 用户文档 将相应更新。
名为 PyUnstable_*
的 C API 的参考文档将自动显示包含指向不稳定层级文档链接的注释。
前导下划线
以前导下划线命名的 C API,以及仅在 Py_BUILD_CORE
下可用的 API,将被视为内部。这意味着
- 它可能会在次要版本(3.x.0,包括 3.x.0 的 Alpha 和 Beta 版本)中无需通知即可更改或删除。补丁版本或候选版本中的 API 更改仅应在绝对必要时进行。
- 它应该仅在源代码注释或开发指南中记录,而不是在公共文档中。
- 在 Python 3.12 之前引入的、已记录在案或在外部广泛使用的 API 应如上所述移动到不稳定层级。
这可能发生在此 PEP 被接受很久之后。因此,在未来几年中,核心开发者在更改带下划线的 API 之前应该进行一些研究,尤其是在它不需要
Py_BUILD_CORE
的情况下。
鼓励 C API 用户在他们的代码库中搜索 _Py
和 _PY
标识符前缀,并将任何匹配项视为需要最终修复的问题——可以通过切换到现有的替代方案,或者通过打开 CPython 问题来请求公开其用例的公共 API,并最终切换到该 API。
初始不稳定 API
以下 API 将在初始实现中移动到不稳定层级,以证明其概念。
代码对象构造函数
PyUnstable_Code_New()
(从PyCode_New
重命名)PyUnstable_Code_NewWithPosOnlyArgs()
(从PyCode_NewWithPosOnlyArgs
重命名)
代码额外信息(PEP 523)
PyUnstable_Eval_RequestCodeExtraIndex()
(从_PyEval_RequestCodeExtraIndex
重命名)PyUnstable_Code_GetExtra()
(从_PyCode_GetExtra
重命名)PyUnstable_Code_SetExtra()
(从_PyCode_SetExtra
重命名)
预计 Python 3.12 中将有更多内容,无需其他 PEP。
向后兼容性
C API 向后兼容性预期将变得更加清晰。
所有重命名的 API 在可行的情况下都将保留旧名称。
如何教授
这些更改会影响高级 C 程序员,他们应该查阅更新的参考文档、开发指南和/或“新增功能”文档。
参考实现
https://github.com/python/cpython/compare/main…encukou:unstable-tier
被拒绝的想法
无特殊前缀
在此 PEP 的初始版本中,不稳定的 API 没有使用 PyUnstable
前缀。相反,定义 Py_USING_UNSTABLE_API
使 API 在给定的源文件中可用,表明承认该文件整体可能需要在每个 Python 版本中重新审视。
但是,后来决定需要在各个名称中暴露不稳定性。
下划线前缀
可以使用前导下划线来标记私有 API 和不稳定 API。但是,这会削弱 _Py
前缀的含义。仅为内部 API 保留该前缀使其易于搜索。
新的头文件目录
其他 API 层级为头文件提供了专用的目录(Include/cpython/
、Include/internal/
)。
由于不稳定层级使用了非常明显的命名约定,并且名称始终可用,因此像 Include/unstable/
这样的目录是不必要的。
Python API
在 Python(而不是 C)API 中添加类似的层级可能会有好处,例如对于 types.CodeType
。但是,该机制需要有所不同。这超出了 PEP 的范围。
版权
本文档放置在公共领域或根据 CC0-1.0-Universal 许可证,以更具许可性的为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0689.rst