首页
/ CPython 3.5.0rc3 发布说明深度解析:import 子系统、内存安全与语言核心修复

CPython 3.5.0rc3 发布说明深度解析:import 子系统、内存安全与语言核心修复

2026-09-09 12:05:56作者:庞队千Virginia

本篇文章以 CPython 仓库中的 Misc/NEWS.d/3.5.0rc3.rst 为准,逐条解读 CPython 3.5.0 第三个候选版本(Release Candidate 3,2015-09-07 发布)修复的 8 个关键问题。候选版本阶段通常只做 Bug 修复,不再引入新特性,因此这份发布说明是研究 CPython 在 3.5 时代对"导入子系统、内存安全、类型系统与集合容器"等核心领域所做收尾工作的第一手资料。读完本文,你将理解 warnings.warn(stacklevel=) 与导入子系统的交互、__class__ 赋值为何受限于可变对象、PEP 448 语法在 AST 编译阶段的坑,以及 BytesIO.readline()deque.index() 等 API 曾经存在的越界风险,并能结合仓库源码与测试用例验证这些修复的实际行为。

一、文档概况与背景

Misc/NEWS.d/3.5.0rc3.rst 是 CPython 源码树中标准的 "blurb" 格式发布说明片段,记录了 3.5.0rc3(第三个发布候选版,发布于 2015-09-07)期间合入的 8 项变更。每一条目都包含四个元数据字段:

  • bpo:关联的 Python bug 跟踪器问题编号(如 24305、24912、24975 等),用于追溯原始讨论与修复补丁;
  • date:blurb 内部使用的整数日期编号(9302~9295),按时间倒序排列,最晚的变更排在最前;
  • nonce:随机标识符,用于区分同一天提交的多个条目;
  • section:变更所属模块分类,本文 8 条中 3 条属于 Core and Builtins(核心与内建),5 条属于 Library(标准库)。

从分类看,3.5.0rc3 的收尾工作覆盖了导入系统、警告系统、AST 编译、类型对象赋值、time.strftimetypingiocollections 等多个方向,其中内存安全类修复(缓冲越界/越读)占了近半数,体现了发布前对安全问题的重视。

二、Core and Builtins:核心解释器层的三处修复

2.1 警告 stacklevel 不再计入导入子系统栈帧(bpo-24305)

Prevent import subsystem stack frames from being counted by the warnings.warn(stacklevel=) parameter.

warnings.warn(message, stacklevel=n) 用于报告"调用链中向上 n 层"的告警位置,在第三方库中常用于把警告定位到用户代码而非库内部。3.5.0rc3 之前,如果警告是在模块导入期间(import subsystem)发出的,导入子系统自身的内部栈帧会被错误地计入 stacklevel,导致告警指向的源码位置(文件与行号)偏离真实调用点。

修复后,导入子系统产生的内部帧不再参与 stacklevel 计数。仓库中保留着对应的回归测试 Lib/test/test_warnings/init.py 中的 test_stacklevel_import(约 635 行起),它先卸载 test.test_warnings.data.import_warning 模块,再重新导入以触发模块级告警,并断言 stacklevel=2 时告警能正确指向模块级位置(注释即标明 "Issue #24305: With stacklevel=2, module-level warnings should work.")。这印证了该修复的真实场景:在模块导入期间用 warnings.warn(..., stacklevel=2) 报告"模块级使用已弃用 API"这类告警时,位置信息必须准确,否则用户会收到指向解释器内部而非自身代码的误导性提示。

2.2 禁止对不可变内建对象执行 __class__ 赋值(bpo-24912)

Prevent class assignment to immutable built-in objects.

Python 允许对实例执行 obj.__class__ = SomeClass 来动态改变对象类型,前提是对象的内存布局兼容。3.5.0rc3 之前,这一操作对某些不可变的内建类型实例(immutable built-in objects,例如 inttuplefrozenset 等类型的不变实例)也可能被放行,从而破坏对象不变量、埋下未定义行为隐患。此次修复从类型层面对赋值目标做了更严格的检查:不可变内建对象的实例不再允许 __class__ 重赋值。

从机制上理解,__class__ 赋值最终会走到对象子系统的类型检查路径(涉及 Objects/object.cObjects/typeobject.c 中对 tp_mro、内存布局兼容性的判断)。对一个不可变对象来说,改变其类型可能违反其值语义(例如 int 实例的哈希、运算都依赖其确切类型),因此禁止是更安全的设计。对使用者而言,这意味着"给整数、元组等不可变内建对象换类型"这类写法在 3.5.0rc3 起会被明确拒绝,应改为显式构造新对象或使用可变类型。

2.3 修正 PEP 448 语法的 AST 编译(bpo-24975)

Fix AST compilation for :pep:448 syntax.

PEP 448("Additional Unpacking Generalizations")是 Python 3.5 的核心新特性,允许在调用、字面量、显示式等更多位置使用 *** 解包,例如:

# 函数调用中的多次解包
f(*a, *b, **c, **d)

# 列表、集合、字典字面量中的多次解包
[*a, *b]
{*a, *b}
{**a, **b}

# 元组显示中的解包
(*a, *b)

该特性在 3.5.0 开发周期早已合入,但直到 rc3 阶段仍发现 AST(抽象语法树)编译环节对某些 PEP 448 语法组合存在缺陷,因此有了本次 "Fix AST compilation" 的收尾修复。相关测试位于 Lib/test/test_ast/test_ast.py(该文件同时被仓库中的 import 相关测试所引用,用于验证导入链路上的 AST 行为)。这一条目提醒我们:即便语言特性已宣布完成,语法树编译这种基础环节的边界情况依然可能在候选发布阶段暴露并修复。

三、Library:标准库的五处修复

3.1 time_strftime 缓冲区越读(bpo-24917)

time_strftime() buffer over-read.

time.strftime(format[, t]) 将时间元组按格式字符串输出。修复前的 C 实现(Modules/timemodule.c 中的 time_strftime)在处理某些格式串/时间值组合时存在**缓冲区越读(buffer over-read)**风险——即读取了分配缓冲区之外的内存。这类缺陷虽然多数时候不会立即崩溃,但属于内存安全漏洞,可能在特定输入下泄露相邻内存内容。rc3 对该路径做了修正,确保读取严格限定在有效缓冲区内。这再次体现发布候选阶段对内存安全问题的高优先级处理。

3.2 imp.load_dynamic() 恢复"忽略已加载模块"语义(bpo-24748)

To resolve a compatibility problem found with py2exe and pywin32, imp.load_dynamic() once again ignores previously loaded modules to support Python modules replacing themselves with extension modules. Patch by Petr Viktorin.

imp.load_dynamic(name, path) 是旧式导入工具(现已由 importlib 取代,但 3.5 时代仍在 Lib/imp.py 及 importlib 的 C 引导层中提供)中用于从指定路径动态加载扩展模块的接口。修复前其行为发生了变化,导致与 py2exepywin32 等打包/扩展生态工具出现兼容性问题。

本次修复将 imp.load_dynamic() 恢复为"忽略先前已加载的同名模块"的旧语义,从而支持如下场景:一个 Python 模块在运行时用扩展模块替换自身(a Python module replacing itself with an extension module),即先以 .py 形式被导入、随后在同名模块尚未从 sys.modules 中清除的情况下,又通过 load_dynamic() 加载同名 .pyd/.so 并接管模块对象。补丁作者是 Petr Viktorin(CPython 核心开发者,长期负责导入系统维护)。

在仓库中,imp 的替代品 importlib 的相关实现位于 Lib/importlib/_bootstrap.pyLib/importlib/_bootstrap_external.pyload_dynamic 的调用方包括 Lib/modulefinder.py 等工具;同时 Lib/test/test_import/init.pyLib/test/test_importlib/source/test_file_loader.py 中保留了与动态加载、扩展替换相关的回归测试。这条修复对理解"模块自替换"这一冷门但有实际需求的导入模式很有价值。

3.3 typing.Iterable 的 isinstance 缓存 Bug(bpo-24635)

Fixed a bug in typing.py where isinstance([], typing.Iterable) would return True once, then False on subsequent calls.

这是 typing 模块(Python 3.5 新引入的"类型提示"标准库,位于 Lib/typing.py)中的一个状态污染 Bug:对同一个实例调用 isinstance([], typing.Iterable),第一次返回 True,随后再调用却返回 False

原因在于 typing.Iterable 这类泛型别名对 isinstance/issubclass 的支持依赖内部缓存(用于记录"哪些具体类型已通过某个泛型别名的检查"),而早期实现存在缓存未正确初始化或未按实例区分的问题,导致首次检查写入的状态影响了后续结果。修复后 isinstance([], typing.Iterable) 在任意多次调用中均稳定返回 True。对于刚在 3.5 中体验类型提示的开发者而言,这类"结果不稳定"的隐蔽 Bug 修复尤为重要——它保证了基于 typing 的运行时检查具备可预期性。

3.4 BytesIO.readline() 位置越界导致缓冲越读(bpo-24989)

Fixed buffer overread in BytesIO.readline() if a position is set beyond size. Based on patch by John Leitch.

io.BytesIO 允许通过 seek() 把文件位置设置到超过缓冲区末尾的位置(size)。修复前的 BytesIO.readline() 在"位置已越界"的状态下被调用时,会读取超出缓冲区的内存(buffer overread),属于又一处内存安全缺陷。补丁基于安全研究员 John Leitch 的提交。

在仓库中,BytesIO 的纯 Python 参考实现位于 Lib/_pyio.py(其 BytesIO/BufferedIOBase 体系中的 readline 定义于 513 行附近的 BufferedIOBase 区段,以及后续 readline(size=None) 的具体实现),底层 C 实现则在 Modules/_io/ 目录下(bytesio.c)。修复要点是:当内部位置已经超过缓冲大小、且 size 参数未显式指定时,应直接返回空结果或按"无更多数据"处理,而不是继续从越界指针读取。对使用者来说,这提醒了"先 seek 越界再 readline"这类非典型操作序列也需要被正确、安全地处理。

3.5 deque.index() 越界参数导致的 overrun 错误(bpo-24913)

Fix overrun error in deque.index(). Found by John Leitch and Bryce Darling.

collections.deque.index(value, [start, [stop]]) 返回 value 在双端队列中的首次出现位置,语义与 list.index() 对齐,start/stop 支持负数与越界值。修复前,当传入的 stop 参数超出队列实际长度时,实现可能发生越界访问(overrun)

当前仓库中 deque_index 的 C 实现位于 Modules/_collectionsmodule.c 的 1245 行起(Argument Clinic 生成的签名:deque.index(value, [start, [stop]])start 默认 0、stop 默认 PY_SSIZE_T_MAX),修复后的边界处理逻辑清晰可见:

if (stop > size)
    stop = size;
if (start > stop)
    start = stop;
assert(0 <= start && start <= stop && stop <= size);

即先把负的 start/stop 按长度折算并夹取到 [0, size],再强制 start <= stop <= size,从根本上杜绝越界。仓库测试 Lib/test/test_deque.py 中保留了专门针对本次缺陷的回归用例 test_index_bug_24913

def test_index_bug_24913(self):
    d = deque('A' * 3)
    with self.assertRaises(ValueError):
        i = d.index("Hello world", 0, 4)

队列只有 3 个元素,却在 stop=4(超出长度)的区间内查找不存在的值,修复后正确抛出 ValueError 而非触发越界。同一测试类还通过暴力对比 deque.index(element, start, stop)list.index(element, start, stop) 在各种 start/stop 组合下的行为一致性(含负数与越界参数),进一步保障边界语义与 list 完全对齐。

四、修复主题归纳与实战启示

4.1 按主题归类的修复矩阵

主题 bpo 位置 性质
警告定位准确性 24305 Core and Builtins 行为修复
__class__ 赋值限制 24912 Core and Builtins 安全加固
PEP 448 AST 编译 24975 Core and Builtins 编译缺陷修复
缓冲区越读 24917 / 24989 Library(time / io) 内存安全修复
导入系统兼容性 24748 Library(imp) 兼容性回退修复
typing 运行时检查 24635 Library(typing) 状态污染修复
容器越界访问 24913 Library(collections) 内存安全修复

三条 "buffer overread/overrun" 类目(24917、24989、24913)占据近半壁江山,说明 rc 阶段的核心关注点是:修复一切可能读取越界内存的路径。这既是发布前安全审计的结果,也为后续版本提供了稳定的内存安全基线。

4.2 对应用开发者的启示

  1. 不要在不可变内建对象上依赖 __class__ 赋值:3.5.0rc3 起该操作被禁止,应使用显式转换(如 int()tuple())构造新对象。
  2. 谨慎使用 imp 系 API 的"模块自替换"模式imp.load_dynamic() 恢复了忽略既有同名模块的旧语义,若你的打包方案依赖 py2exe/pywin32 的此类行为,这一修复正是为兼容性兜底;新代码应优先迁移到 importlib
  3. typing 的运行时 isinstance 检查在 3.5.0rc3 后结果稳定:升级后,依赖 isinstance(x, typing.SomeProtocol) 的守卫逻辑不再出现"第一次 True、第二次 False"的诡异表现。
  4. deque/io 的边界参数可放心传越界值deque.index(value, start, stop)BytesIO 的越界位置读取已被显式夹取或安全短路,行为与 list.index 一致(找不到则抛 ValueError),测试用例 Lib/test/test_deque.py 提供了完整的等价性验证。

五、结语

CPython 3.5.0rc3 的 8 条变更虽然篇幅精炼,却浓缩了发布候选阶段"查漏补缺、加固安全"的典型工作:既有导入子系统与 stacklevel 的交互修复、__class__ 赋值的新限制、PEP 448 语法的 AST 收尾,也有 time_strftimeBytesIO.readline()deque.index() 三处内存安全修复,以及 imp.load_dynamic() 的兼容性回归与 typing.isinstance 的缓存 Bug。对于想要深入研究 CPython 内部机制(导入、对象模型、AST 编译、内存管理)的读者,这条 .rst 记录连同仓库中的 Modules/_collectionsmodule.cLib/_pyio.pyLib/typing.pyLib/importlib/_bootstrap_external.py 以及对应的测试文件,构成了一套"问题—修复—回归验证"的完整学习素材。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
858
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
923
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.83 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
524
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
393