CPython 3.6.4 rc1 发布变更全解析:从 Core 内建、标准库到 C API 的修复清单与源码解读
CPython 3.6.4 rc1 发布变更全解析:从 Core 内建、标准库到 C API 的修复清单与源码解读
CPython 3.6.4 是 Python 3.6 系列在 2017 年底发布的维护版本,其首个发布候选版(rc1,发布日期 2017-12-05)集中修复了 Core and Builtins、标准库、构建系统、IDLE 与 C API 等多个层面的问题。本文以仓库中的官方变更记录(bpo 格式的 NEWS 片段)为骨架,逐节整理 3.6.4rc1 的完整修复清单,并针对代码对象标志、traceback 打印、bytearray 分割、模板字符串、GIL 状态管理等代表性修复,结合当前仓库源码剖析其底层原理。读完本文,你将能读懂 CPython 变更记录的 bpo/NEWS 组织方式,掌握这批修复背后涉及的内部机制,并能在自己的代码中复现和验证这些行为变化。
一、版本背景与 NEWS 文件组织方式
CPython 的每次版本变更都会以独立文件形式记录在 Misc/NEWS.d/ 目录下,文件名为 版本号.rst(如本文主角 3.6.4rc1.rst),每个条目由如下元数据块标识:
.. bpo: 32176 # Python 官方 issue 跟踪系统中的编号
.. date: 2017-12-02-21-37-22 # 提交/录入时间
.. nonce: Wt25-N # 随机字符串,用于合并去重
.. release date: 2017-12-05 # 版本发布日期
.. section: Core and Builtins # 归属章节
其中 section 决定了该条目在最终发布公告(whatsnew / NEWS)中所属的章节。3.6.4rc1 的条目分布在十个章节:Core and Builtins、Library、Documentation、Tests、Build、Windows、macOS、IDLE、Tools/Demos、C API。下面的内容即按这些章节逐一展开。
二、Core and Builtins:解释器核心修复
1. 代码对象标志:CO_NOFREE 由构造器自动维护(bpo-32176)
本版本最重要的内建修复之一是代码对象标志的自动化管理。原变更记录写道:
co_flags.CO_NOFREE现在由代码对象构造器基于 freevars 和 cellvars 自动正确设置,而不再要求调用方手动设置。这确保当额外的 cell 引用被注入到修改过的代码对象和函数中时,该标志会被自动清除。
从当前仓库源码看,CO_* 标志族定义在 Include/cpython/code.h,包括 CO_OPTIMIZED、CO_NEWLOCALS、CO_VARARGS、CO_VARKEYWORDS、CO_NESTED、CO_GENERATOR、CO_COROUTINE 等;而 CO_NOFREE 这一标志在 3.6.4 之后的主线版本中已不再出现在标志列表里(当前 code.h 中搜索不到该宏),说明它属于 3.6.x 时期的过渡性机制,此后被进一步演进或移除。这正体现了本次修复的用意:把"代码对象是否引用自由变量"这个从属信息从调用方的责任中剥离出来,交给构造器根据 co_freevars/co_cellvars 推导,避免注入 cell 后标志与实际情况不一致。
代码对象的构造与标志赋值位于 Objects/codeobject.c,例如构造器会统计 nfreevars 并写入 co->co_nfreevars(codeobject.c),co_flags 则在构造时整体赋值(codeobject.c)。对涉及动态修改 code object 元编程场景(如装饰器、字节码重写)的开发者而言,这项修复意味着不需要再手工维护标志位,减少了标志漂移带来的隐患。
2. traceback 打印与 sys.tracebacklimit(bpo-31949)
本次修复集中处理了回溯打印函数 PyTraceBack_Print() 的一系列边界问题:
- 将
sys.tracebacklimit设为 0 或负数时,现在会抑制回溯打印; - 设为
None时,回退到默认限制; - 设为大于
LONG_MAX的整数时,按LONG_MAX处理而不是使用默认限制; - 修复了 Windows 上超过
2**31个回溯项时的整数溢出; - 修复了输出错误处理。
当前仓库中对应的实现位于 Python/traceback.c 的 _PyTraceBack_Print():它以 PyTraceBack_LIMIT(值为 1000)作为默认上限,通过 PySys_GetOptionalAttrString("tracebacklimit", &limitv) 读取用户设置;若值为 PyLong 则用 PyLong_AsLongAndOverflow() 转换——overflow > 0 时直接取 LONG_MAX,limit <= 0 时立即返回 0(即不打印任何回溯)。这套逻辑正是本变更记录描述的"抑制打印、超大值钳制到 LONG_MAX"行为的落点。
3. 异常链完整打印与交互式解释器稳定性(bpo-28603、bpo-30696)
- bpo-28603(Core and Builtins 部分):解释器退出时现在会打印异常的完整 context/cause 链,即使链中某个异常不可哈希(unhashable)或与后续异常相等,也不会中途丢弃。该修复由 Zane Bitter 提交(同编号在 Library 章节还修复了打印回溯时因异常不可哈希触发的
TypeError,IDLE 章节也有对应修复)。 - bpo-30696:修复了交互式解释器在内存耗尽时无限循环的问题。配合 Tests 章节新增的
_testcapi.set_nomemory(start, stop)与remove_mem_hooks()(bpo-30695),开发者在_testcapi模块中拥有了可编程注入"内存耗尽"状态的测试钩子,为 OOM 路径的回归测试提供了手段。
4. bytearray.partition() / rpartition() 严格类型检查(bpo-20047)
bytearray 的 partition() 和 rpartition() 方法现在只接受 bytes-like 对象作为分隔符,与文档一致。尤其重要的是:当传入整数作为分隔符时,现在抛出 TypeError,而不再返回错误结果。
在 Objects/bytearrayobject.c 中可以看到两个方法的实现骨架:bytearray_partition_impl() 通过 _PyByteArray_FromBufferObject(sep) 将分隔符统一转换为 bytes 对象,再调用 stringlib_partition();如果 sep 不是 bytes-like 对象,转换阶段就会失败并抛错。rpartition 的实现则从尾部开始搜索(bytearrayobject.c)。这提醒使用者:分隔符必须是 bytes/bytearray/memoryview 等类型,绝不能像 str 那样传整数。
5. 解析器与软关键字(bpo-31852、bpo-31592)
- bpo-31852:修复了
async软关键字与续行(continuation line)组合使用时的段错误。async/await作为软关键字在词法层面需要结合上下文判定,该修复堵住了解析路径上的崩溃点。 - bpo-31592:修复了在非法
unicodedata.normalize()调用下 Python 解析器的断言失败(patch by Oren Milman)。
6. 导入系统(bpo-31642、bpo-21720、bpo-31315)
- bpo-31642:恢复
from package import module的阻塞式行为——通过在sys.modules["package.module"]中置入None实现。 - bpo-21720:当
__import__()的fromlist参数或模块的__all__属性中包含 bytes 实例时,不再发出BytesWarning。 - bpo-31315:修复
imp.create_dynamic()在spec.name不是字符串时的断言失败(patch by Oren Milman)。
7. 数值与编解码(bpo-31619、bpo-31825)
- bpo-31619:修复了将包含大量下划线分隔符的字符串按二进制基数转换为整数时抛出的
ValueError。 - bpo-31825:修复了
unicode-escape编解码器和codecs.escape_decode()在解码转义的非 ASCII 字节时抛出OverflowError的问题。
8. 内存分配器(bpo-31626)
修复了调试内存分配器(debug memory allocator)的一个缺陷:内存块收缩(shrink)之后存在"向已释放内存写入"的问题。这对使用 PYTHONMALLOC=debug 排查内存问题的开发者有直接价值——分配器自身必须先保持正确,才能可靠地检测上层 bug。
9. 类创建与类型机制的健壮性(bpo-31588、bpo-31492、bpo-31490、bpo-31566、bpo-31505、bpo-31478、bpo-31311、bpo-31293、bpo-31285、bpo-31592)
这一批修复大多来自 Oren Milman,共同点是"把断言失败/崩溃/错误行为改成带清晰信息的异常":
- bpo-31588:metaclass 的
__prepare__()方法实现不佳导致类创建失败时,现在抛出带提示信息的TypeError。 - bpo-31492:从带非法
__name__属性的模块导入失败、或访问这类模块的属性失败时,不再触发断言失败。 - bpo-31490:
ctypes类定义中,属性名出现在_anonymous_但未出现在_fields_时,断言失败被修复。 - bpo-31566:
_warnings.warn()在遇到非法__name__全局变量时的断言失败被修复。 - bpo-31505:
json的_json.make_encoder()收到非法encoder()参数时的断言失败被修复。 - bpo-31478:
_random.Random.seed()在参数带有非法__abs__()方法时的断言失败被修复。 - bpo-31311:
ctypes._CData.__setstate__()在遇到非法__dict__时的崩溃被修复。 - bpo-31293:
timedelta对象与带有非法as_integer_ratio()方法的 float 做真除法和乘法时的崩溃被修复。 - bpo-31285:
warnings.warn_explicit在 loader 的get_source()返回值带有非法splitlines()方法时的断言失败被修复。
这些修复的共同模式是:外部扩展(C 扩展或用户自定义对象)提供损坏/恶意的特殊方法时,解释器核心不再以断言失败或段错误的方式崩溃,而是安全地以 TypeError 等标准异常报错——这也是 CPython "失败要优雅" 哲学在维护版本中的一贯体现。
10. PyErr_PrintEx 清理被忽略异常(bpo-30817)
PyErr_PrintEx() 现在会清除由 _PySys_SetObjectId() 可能引发的被忽略异常(例如内存耗尽时)。这避免了在异常打印路径上再次叠加异常导致的信息丢失。
三、Library:标准库各模块修复
1. 并发与 I/O:asyncio、io、select、socket、time、threading
asyncio(bpo-32034、bpo-32015、bpo-31970、bpo-31620、bpo-31632):
- bpo-32034:
asyncio.IncompleteReadError与LimitOverrunError现在可被 pickle。 - bpo-32015:修复了在等待 socket 异步读/写期间 socket 重连时 asyncio 事件循环空转(looping)的问题。
- bpo-31970:降低了 asyncio debug 模式的性能开销。
- bpo-31620:空的
asyncio.Queue在其get()轮询者超时后不再泄漏内存。 - bpo-31632:修复
_SSLProtocolTransport.set_protocol()方法——此前它修改的是对 protocol 的错误引用。
io(bpo-32186):io.FileIO.readall() 与 io.FileIO.read() 在获取文件大小时现在会释放 GIL,修复了 NFS 服务器不可达时所有线程挂起的问题(patch by Nir Soffer)。
select/socket(bpo-31786、bpo-30058、bpo-31334、bpo-31893、bpo-31927):
- bpo-31786:修复
select模块中负超时(介于 -1.0 与 0.0 之间)的取整问题——此前被错误地四舍五入导致调用变成非阻塞,现在会正确阻塞等待事件(patch by Pablo Galindo)。同属一类修复的还有 bpo-31806:time.sleep()、threading.Lock.acquire()、socket.socket.settimeout()的负超时取整,此前同样错误地变成非阻塞。 - bpo-30058:修复
select.kqueue.control()的缓冲区溢出。 - bpo-31334:修复
poll.poll([timeout])在只能接受非负整数或 -1 的所有操作系统上对任意负超时的处理(patch by Riccardo Coccioli)。 - bpo-31893:修复 OpenBSD/NetBSD 上
kqueue_event结构的布局以及kqueue_event对象的比较。 - bpo-31927:修复 NetBSD 8 上 socket 模块的编译;修复 NetBSD/DragonFly BSD 上解析
AF_BLUETOOTH地址时的断言失败或读取任意数据问题。
threading(bpo-31516):threading.current_thread() 在解释器关闭(shutdown)时不应再返回 dummy 线程。
faulthandler(bpo-31701):Windows 上 faulthandler.enable() 现在忽略 MSC 与 COM 异常。
2. 数据与格式:plistlib、marshal、codecs、email、typing、random、uuid、netrc、msilib、sqlite3、robotparser
plistlib(bpo-32072、bpo-31897):
- bpo-32072 一口气修复了二进制 plist 的多项问题:支持保存 bytearray;相同对象只保存一次;相等引用加载后成为相同对象;新增对递归数据结构的保存与加载支持。
- bpo-31897:读取二进制 plist 时捕获更多错误,统一抛出
InvalidFileException而不是各类意外异常。
marshal(bpo-32011):恢复加载带 TYPE_INT64 代码的 marshal 文件——这类文件可由 Python 2.7 生成,恢复了 3.x 对旧数据的向后兼容。
codecs(bpo-32110、bpo-31825):codecs.StreamReader.read(n) 对非负 n 现在至多返回 n 个字符/字节,与其他 file-like 对象的 read() 语义保持一致;escape_decode 的溢出问题见 Core 章节。
email(bpo-27240):重写了新 email 策略(policy)的头部折叠(header folding)算法,同时修复 bpo-30788、bpo-31831、bpo-32182;特别是 RFC2231 折叠现在被正确实现。
typing(bpo-28556):两项小修复——允许泛型类实例的浅拷贝;改善 __init_subclass__ 与泛型的交互(原始 PR 来自 Ivan Levkivskyi)。
random(bpo-31482):random.seed() 在 version=1 模式下现在可以处理 bytes 输入。
uuid(bpo-9678):修复 MAC 地址的确定方式:在 NetBSD/OpenBSD 上使用 ifconfig,在 Linux/FreeBSD/NetBSD/OpenBSD 上使用 arp(基于 Takayuki Shimizukawa 的 patch)。
netrc(bpo-30806):修复 netrc 对象的字符串表示。
msilib(bpo-12239、bpo-12382):SummaryInformation.GetProperty 在属性值为 VT_EMPTY 时返回 None(初始 patch by Mark Mc Mahon);OpenDatabase 无法打开/创建 MSI 文件时抛出更友好的异常消息(初始 patch by William Tisäter)。
sqlite3(bpo-31770、bpo-31764):防止对 sqlite3.Cursor 对象重复调用 __init__() 导致崩溃(patch by Oren Milman);防止对未初始化的 Cursor 调用 close() 导致崩溃(同样由 Oren Milman 修复)。
urllib.robotparser(bpo-31325):修复 RobotFileParser.parse() 中对 collections.namedtuple 的错误用法(初始 patch by Robin Wellner)。
string.Template(bpo-31672):string.Template 的 idpattern 此前会匹配部分非 ASCII 字符,现在改用 -i(即内联的 (?a:...) ASCII 局部标志)正则表达式局部标志来排除非 ASCII 字符。对应实现就在 Lib/string/init.py 中:
class Template:
delimiter = '$'
# r'[a-z]' matches to non-ASCII letters when used with IGNORECASE, but
# without the ASCII flag. We can't add re.ASCII to flags because of
# backward compatibility. So we use the ?a local flag and [a-z] pattern.
# See https://bugs.python.org/issue31672
idpattern = r'(?a:[_a-z][_a-z0-9]*)'
源文件中的这段注释完整记录了这个 bpo-31672 修复的技术缘由:直接给 flags 加 re.ASCII 会破坏向后兼容,因此选择在正则内部使用 (?a:...) 局部标志。这是"修复 bug 且不破坏兼容 API"的典型示例。
3. 系统与平台:curses、Tkinter、multiprocessing、signal、inspect、itertools、pickle、logging、ensurepip、venv
curses(bpo-27666、bpo-25720、bpo-31891、bpo-15037):
- bpo-27666:修复
curses.box()与curses.ungetmouse()在chtype或mmask_t类型小于 Clong时造成的栈损坏;curses.box()现在接受字符作为参数(基于 Steve Fink 的 patch)。 - bpo-25720:修复检查 curses WINDOW pad 状态的方法(patch by Masayuki Yamamoto)。
- bpo-31891:修复 NetBSD 上 curses 模块的构建。
- bpo-15037:为 ncurses 5.7 及更早版本的
getkey()添加工作区(workaround)。
Tkinter(bpo-31675、bpo-31673):修复 splitlist() 与 split() 传入大于 2 GiB 字符串时的内存泄漏;修复方法名 adderrorinfo() 的拼写错误。
multiprocessing(bpo-28326、bpo-31310、bpo-31308):修复 multiprocessing.Process 在 stdout 和/或 stderr 被关闭或为 None 时的问题;semaphore tracker 崩溃后应重新启动;forkserver 进程现在对 Ctrl-C 等用户中断免疫,崩溃后按需重启。
signal(bpo-30057):修复 signal.signal() 中可能丢失信号的问题。
inspect(bpo-25532):inspect.unwrap() 现在最多尝试解包 sys.getrecursionlimit() 次,以防范"每次属性访问都创建新对象"的对象导致的死循环。
itertools(bpo-30347):修复并发迭代多个 itertools.groupby() 迭代器时的崩溃。
pickle(bpo-28416):带 persistent_id() 的 Pickler 子类和带 persistent_load() 的 Unpickler 子类实例不再产生引用循环。
logging(bpo-31457,两条):使用嵌套 log adapter 时,内层 process() 方法不再被遗漏;LoggerAdapter 对象的 manager 属性现在可正常赋值。
ensurepip(bpo-31351):python -m ensurepip 在 pip 引导失败时现在以非零退出码退出。
venv(bpo-25351):避免在存在未定义变量时 venv activate 脚本失败。
4. datetime 与其它(bpo-31752、bpo-31728)
- bpo-31752:修复用自定义整数调用
timedelta构造器时可能的崩溃。 - bpo-31728:防止
_elementtree中Element.text与Element.tail的不安全清理导致的崩溃(patch by Oren Milman)。
四、Documentation:文档修正
- bpo-32105:为
asyncio.BaseEventLoop.connect_accepted_socket添加 versionadded 标记。 - bpo-31537:修复 readline 文档示例代码中
get_history_length的错误用法(patch by Brad Smith)。 - bpo-30085:文档明确——
operator模块中不带双下划线的函数(如operator.add)更推荐用于可读性,带双下划线的变体仅保留以维持向后兼容。
五、Tests:测试基建调整
- bpo-31380:
test_httpservers的test_undecodable_file在 macOS 上跳过——该测试在 APFS 文件系统上失败。 - bpo-31705:
test_socket.test_sha256()在早于 4.5 的 Linux 内核上跳过——该测试在内核 3.10(ppc64le)上以ENOKEY失败,修复已并入内核 4.5。 - bpo-31174:修复
test_tools.test_unparse的DirectoryTestCase,现在固定存储样例名称以确保总是测试相同的文件,避免追踪引用泄漏时出现误报。 - bpo-30695:为
_testcapi模块新增set_nomemory(start, stop)与remove_mem_hooks()函数(配合 Core 章节的 bpo-30696 OOM 修复)。
六、Build:构建系统修复
- bpo-32059:
setup.py的detect_modules()在交叉编译时现在也会搜索 sysroot 路径。 - bpo-31957:修复 Windows 构建时的 Windows SDK 版本检测。
- bpo-31609:修复 PCbuild/clean.bat 中的引号问题。
- bpo-31934:在非干净的源码树外构建时直接中止构建。
- bpo-31926:修复 Argument Clinic 在单个 .c 文件中存在多个同名函数/方法时偶尔导致的编译错误。
- bpo-28791(两条):Windows 构建与 OS X 安装包均更新为使用 SQLite 3.21.0。
- bpo-22140:防止 Misc/python-config.sh.in 中 prefix 被双重替换。
- bpo-31536:
make regen-all在没有实际变化时避免整体重建。
七、Windows 与 macOS 专项修复
Windows(bpo-1102、bpo-31944):
- bpo-1102:
View.Fetch()返回ERROR_NO_MORE_ITEMS时,现在返回None而不是抛出MSIError(初始 patch by Anthony Tuininga)。 - bpo-31944:修复 Windows "应用和功能(Apps and Features)"对话框中的 Modify 按钮。
macOS(bpo-31392):macOS 安装包更新为使用 OpenSSL 1.0.2m。
八、IDLE:编辑器与 Shell 改进
IDLE 在本版本获得大量改进,涵盖异常回溯、模块浏览器与界面细节:
- bpo-32207:改善 IDLE 中 tk 事件异常的 traceback——当 tk 事件处理由 IDLE 的运行循环驱动时,不再附加令人困惑的
queue.EMPTYtraceback 上下文,现在与用户代码驱动事件处理时的 traceback 一致(基于 Serhiy Storchaka 的建议)。 - bpo-32164:删除无用文件
idlelib/tabbedpages.py,configdialog 中的TabbedPageSet用法被ttk.Notebook取代。 - bpo-32100:修复 pathbrowser 的新旧 bug 并改进测试(patch 主要来自 Cheryl Sabella)。
- bpo-31858:将 Shell 提示符操作限制在 Shell 窗口内——编辑器和输出窗口只看到空的最后一行提示符,简化代码并修复插入换行时的小 bug;
sys.ps1若存在则只在 Shell 启动时读取,不再设置或修改。 - bpo-31860:IDLE 配置对话框中字体示例现在可编辑,改动在 IDLE 保持打开期间持续生效。
- bpo-31836:修复
test_code_module在test_idle之后运行失败的问题——code模块在sys.ps1存在时使用它,否则设为'>>> ',测试现在正确覆盖两种行为,ps2同理。 - bpo-28603(IDLE 条目):修复打印包含不可哈希异常对象的 traceback 时导致 Shell 重启的
TypeError(patch by Zane Bitter)。 - bpo-13802:IDLE 字体设置示例改用非拉丁字符——即使所选字体只覆盖 Unicode BMP 的有限子集,tcl/tk 也会用其它字体补全字符,扩展后的示例让非拉丁字符用户对 IDLE Shell 和编辑器中的显示效果有更好的预期;为此 Font 标签页的框架重新排布,Font/Tabs 帮助也补充了说明。
- bpo-31460:简化 IDLE 模块浏览器(Module Browser)的 API——传 widget 而非带 root widget 的 flist,为"浏览器框架仅作为窗口一部分"铺路;传完整文件名而非假定来自 .py 文件的片段,使浏览非 .py 结尾的 Python 文件成为可能。
- bpo-31649:
_htest、_utest参数改为仅关键字(keyword-only)。 - bpo-31559:消除
idle_test.test_browser的测试顺序依赖。 - bpo-31459:将 IDLE 的 Class Browser 更名为 Module Browser——模块级类与方法浏览器早在多年前就已加入模块级函数成为模块浏览器,昨日又加入嵌套类与函数;为向后兼容,设置对话框 Keys 标签页中出现的虚拟事件
<<open-class-browser>>保持不变(patch by Cheryl Sabella)。 - bpo-31500:默认字体在 HiDPI 显示器上现在按比例缩放。
- bpo-1612262:IDLE 模块浏览器现在显示嵌套类与函数(代码与测试的原始 patch 分别来自 Guilherme Polo 与 Cheryl Sabella)。
九、Tools/Demos:redemo 修复(bpo-30722)
redemo(正则表达式演示工具)现在可在 Python 3.6 及更新版本下工作;同时移除了 LOCALE 选项——因为它在 Python 3 中无法配合字符串模式使用(patch by Christoph Sarnowski)。
十、C API:嵌入与扩展开发者的三个关键修复
1. PyGILState_Ensure() 的初始化顺序(bpo-20891)
修复了 PyGILState_Ensure() 的一个崩溃场景:当它在 PyEval_InitThreads() 之前被非 Python 线程调用时,现在只在 PyThreadState_New() 之后才调用 PyEval_InitThreads(),以修复崩溃。当前仓库中 PyGILState_Ensure() 的实现位于 Python/pystate.c,其注释明确说明"这里不做 Python 自动初始化——除了两个线程自动初始化之间的潜在竞态外,PEP 311 也规定了其它问题,嵌入方应已调用 Py_Initialize()";随后若当前线程没有线程状态,则新建并绑定 thread state,再根据 holds_gil() 的结果决定是否通过 PyEval_RestoreThread() 获取 GIL,最后递增 gilstate_counter。理解这个调用顺序(先建线程状态、后初始化 GIL),对编写正确的多线程嵌入代码至关重要。
2. getpath.c 的分配器混用(bpo-31532)
修复了 Py_GetPath() 与 Py_SetPath() 之间因分配器混用导致的内存损坏。这提醒嵌入开发者:路径相关 API 返回的内存必须用与之匹配的分配/释放接口处理。
3. PyExc_RecursionErrorInst 单例移除(bpo-30697)
PyExc_RecursionErrorInst 单例被移除,PyErr_NormalizeException() 不再使用它。该单例是持久对象,其成员从未被清理,可能在解释器终结阶段引发段错误(参见 issue #22898)。对需要深挖递归错误规范化路径的读者,这是理解"为什么不再缓存 RecursionError 实例"的关键背景。
十一、总结:如何进一步追踪这些修复
3.6.4rc1 是一次以"稳定性和健壮性"为主题的维护发布:Core 层面的重点是代码对象标志自动化(bpo-32176)、traceback 打印边界(bpo-31949)、OOM 下交互解释器与 PyErr_PrintEx 的容错(bpo-30696、bpo-30817),以及 Oren Milman 贡献的一大批"断言失败→友好异常"改造;Library 层面则覆盖 asyncio、plistlib、email、curses、multiprocessing、select/socket 超时取整等真实用户场景的修复。
若想进一步深入,可以:
- 以 bpo 编号为线索,在 Misc/NEWS.d/3.6.4rc1.rst 中定位条目并对比其它版本 NEWS 片段,理解修复的演进脉络;
- 在 Lib/test 中搜索对应模块的测试(如
test_traceback、test_bytearray、test_select),查看这些修复对应的回归测试断言; - 阅读 Python/traceback.c、Objects/codeobject.c、Objects/bytearrayobject.c、Lib/string/init.py、Python/pystate.c 等核心实现文件,验证文中描述的行为边界。
需要说明的是:本文所有行为描述均以 3.6.4rc1 变更记录及其后在主线源码中的对应实现为准;部分内部机制(如 CO_NOFREE 标志)在后继版本中已进一步演进或移除,阅读代码时请以对应版本的实际定义为准。