diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 18ade18d1a1708c..5c58ceeb69f441c 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"): + 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..e0afa3e629782df --- /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`. 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; }