PEP 687 – 标准库中模块的隔离
- 作者:
- Erlend Egeberg Aasland <erlend at python.org>, Petr Viktorin <encukou at gmail.com>
- 讨论至:
- Discourse 帖子
- 状态:
- 已接受
- 类型:
- 标准跟踪
- 要求:
- 489, 573, 630
- 创建日期:
- 2022年4月4日
- Python 版本:
- 3.12
- 发布历史:
- 2022年4月4日, 2022年4月11日
- 决议:
- Discourse 消息
摘要
标准库中的扩展将转换为多阶段初始化(PEP 489),并且在可能的情况下,所有状态都将存储在模块对象上,而不是存储在进程全局变量中。
关于回溯的说明
本提案的大部分内容已经实现。我们提交此PEP以解释这些更改,寻求对它们是否有利的共识,提出剩余的更改,并为新模块设定最佳实践。
动机与原理
信息性的PEP 630描述了拟议更改的背景、动机、原理、影响和实现说明,因为它们通常适用于任何扩展模块(不仅仅是标准库)。
它是本提案的组成部分。请先阅读它。
此PEP讨论了标准库的具体细节。
规范
PEP 630 的正文将转换为Python文档中的HOWTO,并且该PEP将被废弃(标记为最终)。
标准库中的所有扩展模块都将转换为PEP 489中引入的多阶段初始化。
所有标准库扩展模块都将是*隔离的*。也就是说
- 模块定义的类型、函数和其他对象要么是不可变的,要么不与其他模块实例共享。
- 模块特有的状态不会与其他模块实例共享,除非它表示全局状态。
例如,
_csv.field_size_limit将获取/设置模块特定的数字。另一方面,像readline.get_history_item或os.getpid这样的函数将继续处理进程全局(模块外部,并可能在其他库(包括非Python库)之间共享)的状态。
转换为堆类型
不需要模块状态访问且没有其他理由转换的静态类型应保持静态。
其方法需要访问其模块实例的类型将根据PEP 630转换为堆类型,并考虑以下事项
- 所有以前是静态类型的标准库类型都应保持不可变。堆类型必须使用
Py_TPFLAGS_IMMUTABLE_TYPE标志定义以保持不可变性。请参阅bpo-43908。测试应确保在尝试创建不可变类型的新属性时引发
TypeError。 tp_new = NULL的静态类型没有公共构造函数,但堆类型从基类继承构造函数。确保以前无法实例化的类型保留该特性;使用Py_TPFLAGS_DISALLOW_INSTANTIATION。添加使用test.support.check_disallow_instantiation()的测试。请参阅bpo-43916。- 转换后的堆类型可能会无意中变为可序列化(可
pickle)。测试调用pickle.dumps在转换前后是否具有相同的结果,如果测试失败,则添加一个引发TypeError的__reduce__方法。有关示例,请参阅PR-21002。
这些问题将添加到开发指南中,以帮助未来的任何转换。
如果发现其他类型的问题,相关模块应保持不变,直到找到解决方案并添加到开发指南中,并且已转换的模块经过检查和修复。
流程
以下过程应添加到开发指南中,并保持不变,直到所有模块都已转换。任何新发现都应记录在那里或通用HOWTO中。
第1部分:准备
- 在错误跟踪器或Discourse上发起讨论。让模块维护者和/或代码所有者参与。解释更改的原因和原理。
- 识别全局状态性能瓶颈。创建概念验证实现,并测量性能影响。
pyperf是一个很好的基准测试工具。 - 创建实施计划。对于类型较少的小模块,一个PR可能就足够了。对于类型较多,并且可能还有外部库回调的较大模块,将需要多个PR。
第2部分:实现
注意:这是基于从其他模块吸取的经验教训,针对复杂模块的建议实现计划。对于较小的模块,请随意简化。
- 尽可能添加Argument Clinic;它使您能够轻松地使用定义类从类型方法中获取模块状态。
- 准备模块状态;建立一个模块状态
struct,添加一个实例作为静态全局变量,并创建用于获取模块状态的辅助存根。 - 将相关的全局变量添加到模块状态
struct,并修改访问全局状态的代码以使用模块状态辅助函数。此步骤可以分解为多个PR。 - 在必要时,将静态类型转换为堆类型。
- 将全局模块状态结构转换为真正的模块状态。
- 实现多阶段初始化。
步骤4到6最好在一个alpha开发阶段完成。
向后兼容性
标准库中的扩展模块现在可以多次加载。例如,从sys.modules中删除此类模块并重新导入它将导致一个新的模块实例,与任何先前加载的实例隔离。
这可能会影响预期先前行为的代码:扩展模块的全局变量是从第一个加载的模块中浅层复制的。
安全隐患
未知。
如何教授此内容
本提案的很大一部分是面向经验用户的HOWTO,它将被移至文档中。
初学者不应受到影响。
参考实现
大部分更改现在都在主分支中,作为这些问题的提交
- bpo-40077,将静态类型转换为堆类型:使用PyType_FromSpec()
- bpo-46417,为嵌入式Python在Py_Finalize()中清除静态类型
- bpo-1635741,Py_Finalize()在退出时不会清除所有Python对象
例如,在_csv模块中完成的更改和修复是
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0687.rst