From 5f65d54cd9ec90b2e6c93de96e744806eab73ae7 Mon Sep 17 00:00:00 2001 From: lipengyu Date: Thu, 9 Jul 2026 20:23:51 +0800 Subject: [PATCH 1/3] gh-153418: Fix FrameLocalsProxy error propagation in update and |= --- Lib/test/test_frame.py | 10 ++++++++++ ...0-00-00.gh-issue-153418.framelocalsproxy-errors.rst | 3 +++ Objects/frameobject.c | 8 ++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 18ade18d1a1708c..f47133795fa65c3 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -18,6 +18,11 @@ from test import mapping_tests +class FailingKeysDict(dict): + def keys(self): + raise RuntimeError("keys() failed") + + class ClearTest(unittest.TestCase): """ Tests for frame.clear(). @@ -380,6 +385,9 @@ def test_as_dict(self): self.assertEqual(d['x'], 3) self.assertEqual(d['z'], 4) + with self.assertRaisesRegex(RuntimeError, r"keys\(\) failed"): + d.update(FailingKeysDict()) + with self.assertRaises(TypeError): d.update([1, 2]) @@ -399,6 +407,8 @@ def test_as_number(self): self.assertEqual(d['z'], 3) d |= {'y': 3} self.assertEqual(d['y'], 3) + with self.assertRaisesRegex(RuntimeError, r"keys\(\) failed"): + operator.ior(d, FailingKeysDict()) with self.assertRaises(TypeError): d |= 3 with self.assertRaises(TypeError): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst new file mode 100644 index 000000000000000..0d5f1876cd1e445 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst @@ -0,0 +1,3 @@ +Fix ``frame.f_locals |= ...`` mishandling exceptions raised by the right-hand +mapping, and fix ``frame.f_locals.update()`` unconditionally replacing any +exception with :exc:`TypeError`. \ No newline at end of file diff --git a/Objects/frameobject.c b/Objects/frameobject.c index e7ac59379dcfbcc..3fa9b6494d4144b 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -573,7 +573,7 @@ framelocalsproxy_inplace_or(PyObject *self, PyObject *other) } if (framelocalsproxy_merge(self, other) < 0) { - Py_RETURN_NOTIMPLEMENTED; + return NULL; } return Py_NewRef(self); @@ -720,11 +720,15 @@ static PyObject* framelocalsproxy___contains__(PyObject *self, PyObject *key) static PyObject* framelocalsproxy_update(PyObject *self, PyObject *other) { - if (framelocalsproxy_merge(self, other) < 0) { + if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) { PyErr_SetString(PyExc_TypeError, "update() argument must be dict or another FrameLocalsProxy"); return NULL; } + if (framelocalsproxy_merge(self, other) < 0) { + return NULL; + } + Py_RETURN_NONE; } From 35b26213047c72e743e5043a64dd57dae94f7443 Mon Sep 17 00:00:00 2001 From: lipengyu Date: Thu, 9 Jul 2026 20:44:15 +0800 Subject: [PATCH 2/3] update --- ...6-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst index 0d5f1876cd1e445..e0afa3e629782df 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-00-00-00.gh-issue-153418.framelocalsproxy-errors.rst @@ -1,3 +1,3 @@ Fix ``frame.f_locals |= ...`` mishandling exceptions raised by the right-hand mapping, and fix ``frame.f_locals.update()`` unconditionally replacing any -exception with :exc:`TypeError`. \ No newline at end of file +exception with :exc:`TypeError`. From d281115514861877852c33a2e51ff458510daeb7 Mon Sep 17 00:00:00 2001 From: stevens Date: Fri, 10 Jul 2026 09:11:04 +0800 Subject: [PATCH 3/3] apply suggestion Co-authored-by: Brij Kapadia <97006829+brijkapadia@users.noreply.github.com> --- Lib/test/test_frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index f47133795fa65c3..5c58ceeb69f441c 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -408,7 +408,7 @@ def test_as_number(self): d |= {'y': 3} self.assertEqual(d['y'], 3) with self.assertRaisesRegex(RuntimeError, r"keys\(\) failed"): - operator.ior(d, FailingKeysDict()) + d |= FailingKeysDict() with self.assertRaises(TypeError): d |= 3 with self.assertRaises(TypeError):