FrameLocalsProxy `|=` and `update()` mishandle errors raised while merging mappings
还没有人认领这个 Issue。
- 主要语言
- Python
- 星标
- 77.2k
- 派生
- 35.9k
- PR 合并指标
- PR 指标待抓取
描述
Bug report
Bug description:
Bug description
FrameLocalsProxy does not correctly propagate exceptions raised while merging a dict subclass or another supported mapping-like object.
There are two related problems in Objects/frameobject.c:
framelocalsproxy_inplace_or()returnsPy_NotImplementedwhenframelocalsproxy_merge()fails. If the merge failed because Python code raised an exception, the slot returns a normal object while an exception is still set. In a pydebug build this triggers a fatal_Py_CheckSlotResulterror.framelocalsproxy_update()replaces any failure fromframelocalsproxy_merge()with a genericTypeError, masking the original exception.
This is a sub-issue of https://github.com/python/cpython/issues/146102 with gist details
A minimal reproducer is below.
import operator
import sys
class BadDict(dict):
def keys(self):
raise RuntimeError("keys() failed!")
def repro_update():
locs = sys._getframe().f_locals
print(type(locs))
try:
locs.update(BadDict())
except TypeError as exc:
print("BUG: update() masked RuntimeError as TypeError:", exc)
except RuntimeError as exc:
print("OK: update() propagated RuntimeError:", exc)
def repro_inplace_or():
locs = sys._getframe().f_locals
print(type(locs))
try:
operator.ior(locs, BadDict())
except RuntimeError as exc:
print("OK: |= propagated RuntimeError:", exc)
except BaseException as exc:
print("BUG: |= raised wrong exception:", type(exc).__name__, exc)
else:
print("BUG: |= swallowed RuntimeError")
repro_update()
repro_inplace_or()
operator.ior(locs, BadDict()) is used to exercise the same nb_inplace_or slot as locs |= BadDict().
Actual behavior
For .update(), the original RuntimeError from BadDict.keys() is replaced with a generic TypeError:
<class 'FrameLocalsProxy'>
BUG: update() masked RuntimeError as TypeError: update() argument must be dict or another FrameLocalsProxy
For |= on a pydebug build, the interpreter aborts because the slot reports success while an exception remains set:
<class 'FrameLocalsProxy'>
Fatal Python error: _Py_CheckSlotResult: Slot |= of type FrameLocalsProxy succeeded with an exception set
Python runtime state: initialized
Traceback (most recent call last):
File "/tmp/repro_ior.py", line 6, in keys
raise RuntimeError("keys() failed!")
RuntimeError: keys() failed!
Aborted (core dumped)
Expected behavior
Both FrameLocalsProxy.update() and FrameLocalsProxy.__ior__() should propagate the original exception raised while merging:
<class 'FrameLocalsProxy'>
OK: update() propagated RuntimeError: keys() failed!
<class 'FrameLocalsProxy'>
OK: |= propagated RuntimeError: keys() failed!
I will submit a PR to fix this
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
- gh-153421
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 Objects/frameobject.c 中的 framelocalsproxy_inplace_or() 和 framelocalsproxy_update() 开始,然后运行 BadDict.keys() 引发 RuntimeError 的最小复现程序。完成的标准是两个操作都传播原始异常,且不会在 debug build 中发生 slot failure,并为报告的行为提供回归覆盖。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- c, python
- 领域
- backend
- Issue 类型
- 缺陷
- 难度
- 3/5
- 预计耗时
- 1-2 天
- 活跃度
- 停滞
- 描述清晰度
- 描述清楚
- 新手友好度
- 25/100