-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
gh-112716: Fix SystemError when __builtins__ is not a dict #112770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -832,6 +832,30 @@ 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})}) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I wrote previously, I would prefer to check for the expected superglobal value here.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to change also existing tests?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm only proposing to change this test. |
||
| # 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): | ||
|
serhiy-storchaka marked this conversation as resolved.
|
||
| 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): | ||
| savestdout = sys.stdout | ||
| sys.stdout = None # Whatever that cannot flush() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Fix SystemError in the ``import`` statement and in ``__reduce__()`` methods | ||
| of builtin types when ``__builtins__`` is not a dict. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it worth adding a fast path for the case where PyDict_CheckExact(builtins)?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Backports will be more complicated.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PyMapping_GetOptionalItem() has a fast-path for PyDict_CheckExact(). IMO it's enough. |
||
| return NULL; | ||
| } | ||
| if (import_func == NULL) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you also add some test cases where the builtins are a subclass of dict, ensuring that an overridden
__getitem__gets used?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is covered by
test_exec_globals_error_on_get.