diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 18ade18d1a1708..b357ce262702a7 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -18,6 +18,11 @@ from test import mapping_tests +class FailingKeys(dict): + def keys(self): + raise RuntimeError("keys failed") + + class ClearTest(unittest.TestCase): """ Tests for frame.clear(). @@ -380,7 +385,12 @@ def test_as_dict(self): self.assertEqual(d['x'], 3) self.assertEqual(d['z'], 4) - with self.assertRaises(TypeError): + with self.assertRaisesRegex(RuntimeError, "keys failed"): + d.update(FailingKeys()) + + with self.assertRaisesRegex( + TypeError, r"update\(\) argument must be dict or another FrameLocalsProxy" + ): d.update([1, 2]) self.assertEqual(d.setdefault('x', 5), 3) @@ -399,6 +409,8 @@ def test_as_number(self): self.assertEqual(d['z'], 3) d |= {'y': 3} self.assertEqual(d['y'], 3) + with self.assertRaisesRegex(RuntimeError, "keys failed"): + d |= FailingKeys() with self.assertRaises(TypeError): d |= 3 with self.assertRaises(TypeError): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-12-25-06.gh-issue-153418.taxDqe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-12-25-06.gh-issue-153418.taxDqe.rst new file mode 100644 index 00000000000000..11c88c2850509b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-09-12-25-06.gh-issue-153418.taxDqe.rst @@ -0,0 +1,4 @@ +Fix error handling in the ``FrameLocalsProxy`` returned by +:attr:`frame.f_locals`. Exceptions raised by the argument of ``update()`` +or ``|=`` are now propagated instead of being replaced with +:exc:`TypeError` or :exc:`SystemError`, or aborting debug builds. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index e7ac59379dcfbc..40b39a83b485c1 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -325,6 +325,8 @@ static int framelocalsproxy_merge(PyObject* self, PyObject* other) { if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) { + PyErr_SetString(PyExc_TypeError, + "update() argument must be dict or another FrameLocalsProxy"); return -1; } @@ -573,7 +575,7 @@ framelocalsproxy_inplace_or(PyObject *self, PyObject *other) } if (framelocalsproxy_merge(self, other) < 0) { - Py_RETURN_NOTIMPLEMENTED; + return NULL; } return Py_NewRef(self); @@ -721,7 +723,6 @@ static PyObject* framelocalsproxy_update(PyObject *self, PyObject *other) { if (framelocalsproxy_merge(self, other) < 0) { - PyErr_SetString(PyExc_TypeError, "update() argument must be dict or another FrameLocalsProxy"); return NULL; }