FrameLocalsProxy `|=` and `update()` mishandle errors raised while merging mappings
まだ誰も着手していません。
- 主要言語
- 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 にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- 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