From ae68655522fe054166f3489e66ec99bf05f8ee46 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 5 Dec 2023 20:42:05 +0200 Subject: [PATCH 1/4] gh-112716: Fix SystemError when __builtins__ is not a dict --- Lib/test/test_builtin.py | 25 +++++++++++++++++++ ...-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst | 2 ++ Python/ceval.c | 4 +-- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 535856adaea4d3..82a34e11da9d45 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -832,6 +832,31 @@ class customdict(dict): # this one should not do anything fancy self.assertRaisesRegex(NameError, "name 'superglobal' is not defined", exec, code, {'__builtins__': customdict()}) + def test_exec_builtins_mapping(self): + code = compile("superglobal", "test", "exec") + # works correctly + exec(code, {'__builtins__': types.MappingProxyType({'superglobal': 1})}) + # custom builtins mapping is missing key + ns = {'__builtins__': types.MappingProxyType({})} + self.assertRaisesRegex(NameError, "name 'superglobal' is not defined", + exec, code, ns) + + def test_exec_builtins_mapping_import(self): + code = compile("import foo.bar", "test", "exec") + ns = {'__builtins__': types.MappingProxyType({})} + self.assertRaisesRegex(ImportError, "__import__ not found", exec, code, ns) + ns = {'__builtins__': types.MappingProxyType({'__import__': lambda *args: args})} + exec(code, ns) + self.assertEqual(ns['foo'], ('foo.bar', ns, ns, None, 0)) + + def test_eval_builtins_mapping_reduce(self): + code = compile("iter([1, 2]).__reduce__()", "test", "eval") + ns = {'__builtins__': types.MappingProxyType({})} + self.assertRaisesRegex(NameError, "name 'iter' is not defined", + eval, code, ns) + ns = {'__builtins__': types.MappingProxyType({'iter': iter})} + self.assertEqual(eval(code, ns), (iter, ([1, 2],), 0)) + def test_exec_redirected(self): savestdout = sys.stdout sys.stdout = None # Whatever that cannot flush() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst new file mode 100644 index 00000000000000..44d63269c5424a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst @@ -0,0 +1,2 @@ +Fix SystemError in the ``import`` statement and in ``__reduce__()`` methods +of builtin types when ``__builtins__`` is not a dict. diff --git a/Python/ceval.c b/Python/ceval.c index 1806ceb7fa9681..84474e52913e0c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2415,7 +2415,7 @@ PyObject * _PyEval_GetBuiltin(PyObject *name) { PyObject *attr; - if (PyDict_GetItemRef(PyEval_GetBuiltins(), name, &attr) == 0) { + if (PyMapping_GetOptionalItem(PyEval_GetBuiltins(), name, &attr) == 0) { PyErr_SetObject(PyExc_AttributeError, name); } return attr; @@ -2568,7 +2568,7 @@ import_name(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *name, PyObject *fromlist, PyObject *level) { PyObject *import_func; - if (PyDict_GetItemRef(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) { + if (PyMapping_GetOptionalItem(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) { return NULL; } if (import_func == NULL) { From 734811c5a3f71d50fb75243bd63442e0dc528aa7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Dec 2023 10:03:49 +0200 Subject: [PATCH 2/4] Fix test_eval_builtins_mapping_reduce. --- Lib/test/test_builtin.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 82a34e11da9d45..9e39a3f1229229 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -850,11 +850,10 @@ def test_exec_builtins_mapping_import(self): self.assertEqual(ns['foo'], ('foo.bar', ns, ns, None, 0)) def test_eval_builtins_mapping_reduce(self): - code = compile("iter([1, 2]).__reduce__()", "test", "eval") - ns = {'__builtins__': types.MappingProxyType({})} - self.assertRaisesRegex(NameError, "name 'iter' is not defined", - eval, code, ns) - ns = {'__builtins__': types.MappingProxyType({'iter': iter})} + code = compile("x.__reduce__()", "test", "eval") + ns = {'__builtins__': types.MappingProxyType({}), 'x': iter([1, 2])} + self.assertRaisesRegex(AttributeError, "iter", eval, code, ns) + ns = {'__builtins__': types.MappingProxyType({'iter': iter}), 'x': iter([1, 2])} self.assertEqual(eval(code, ns), (iter, ([1, 2],), 0)) def test_exec_redirected(self): From cdbf8e1114eb52f4fa48e70a814dbd42bbcda7db Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Dec 2023 10:45:34 +0200 Subject: [PATCH 3/4] Add a comment. --- Lib/test/test_builtin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 9e39a3f1229229..ed2506eb1cafa2 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -850,6 +850,7 @@ def test_exec_builtins_mapping_import(self): self.assertEqual(ns['foo'], ('foo.bar', ns, ns, None, 0)) def test_eval_builtins_mapping_reduce(self): + # list_iterator.__reduce__() calls _PyEval_GetBuiltin("iter") code = compile("x.__reduce__()", "test", "eval") ns = {'__builtins__': types.MappingProxyType({}), 'x': iter([1, 2])} self.assertRaisesRegex(AttributeError, "iter", eval, code, ns) From e0834e014612a90351dab512fd2a2a64e87e0875 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Dec 2023 20:52:23 +0200 Subject: [PATCH 4/4] Use eval(). --- Lib/test/test_builtin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index ed2506eb1cafa2..88fc1863eb7d2e 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -832,14 +832,15 @@ class customdict(dict): # this one should not do anything fancy self.assertRaisesRegex(NameError, "name 'superglobal' is not defined", exec, code, {'__builtins__': customdict()}) - def test_exec_builtins_mapping(self): - code = compile("superglobal", "test", "exec") + def test_eval_builtins_mapping(self): + code = compile("superglobal", "test", "eval") # works correctly - exec(code, {'__builtins__': types.MappingProxyType({'superglobal': 1})}) + ns = {'__builtins__': types.MappingProxyType({'superglobal': 1})} + self.assertEqual(eval(code, ns), 1) # custom builtins mapping is missing key ns = {'__builtins__': types.MappingProxyType({})} self.assertRaisesRegex(NameError, "name 'superglobal' is not defined", - exec, code, ns) + eval, code, ns) def test_exec_builtins_mapping_import(self): code = compile("import foo.bar", "test", "exec")