[3.12] gh-119821: Support non-dict globals in LOAD_FROM_DICT_OR_GLOBALS (#119822) (#119890)

The implementation basically copies LOAD_GLOBAL. Possibly it could be deduplicated,
but that seems like it may get hairy since the two operations have different operands.

This is important to fix in 3.14 for PEP 649, but it's a bug in earlier versions too,
and we should backport to 3.13 and 3.12 if possible.

(cherry picked from commit 80a4e38994)
This commit is contained in:
Jelle Zijlstra 2024-05-31 21:56:38 -07:00 committed by GitHub
parent 2f7fada580
commit 6d9677d78e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 363 additions and 329 deletions

View file

@ -1,4 +1,5 @@
import pickle import pickle
import textwrap
import types import types
import unittest import unittest
from test.support import check_syntax_error, run_code from test.support import check_syntax_error, run_code
@ -328,3 +329,22 @@ class TypeAliasPickleTest(unittest.TestCase):
with self.subTest(thing=thing, proto=proto): with self.subTest(thing=thing, proto=proto):
with self.assertRaises(pickle.PickleError): with self.assertRaises(pickle.PickleError):
pickle.dumps(thing, protocol=proto) pickle.dumps(thing, protocol=proto)
class TypeParamsExoticGlobalsTest(unittest.TestCase):
def test_exec_with_unusual_globals(self):
class customdict(dict):
def __missing__(self, key):
return key
code = compile("type Alias = undefined", "test", "exec")
ns = customdict()
exec(code, ns)
Alias = ns["Alias"]
self.assertEqual(Alias.__value__, "undefined")
code = compile("class A: type Alias = undefined", "test", "exec")
ns = customdict()
exec(code, ns)
Alias = ns["A"].Alias
self.assertEqual(Alias.__value__, "undefined")

View file

@ -0,0 +1,2 @@
Fix execution of :ref:`annotation scopes <annotation-scopes>` within classes
when ``globals`` is set to a non-dict. Patch by Jelle Zijlstra.

View file

@ -1196,27 +1196,33 @@ dummy_func(
} }
} }
if (v == NULL) { if (v == NULL) {
v = PyDict_GetItemWithError(GLOBALS(), name); if (PyDict_CheckExact(GLOBALS())
if (v != NULL) { && PyDict_CheckExact(BUILTINS()))
{
v = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(),
(PyDictObject *)BUILTINS(),
name);
if (v == NULL) {
if (!_PyErr_Occurred(tstate)) {
/* _PyDict_LoadGlobal() returns NULL without raising
* an exception if the key doesn't exist */
format_exc_check_arg(tstate, PyExc_NameError,
NAME_ERROR_MSG, name);
}
ERROR_IF(true, error);
}
Py_INCREF(v); Py_INCREF(v);
} }
else if (_PyErr_Occurred(tstate)) {
goto error;
}
else { else {
if (PyDict_CheckExact(BUILTINS())) { /* Slow-path if globals or builtins is not a dict */
v = PyDict_GetItemWithError(BUILTINS(), name);
if (v == NULL) { /* namespace 1: globals */
if (!_PyErr_Occurred(tstate)) { v = PyObject_GetItem(GLOBALS(), name);
format_exc_check_arg( if (v == NULL) {
tstate, PyExc_NameError, ERROR_IF(!_PyErr_ExceptionMatches(tstate, PyExc_KeyError), error);
NAME_ERROR_MSG, name); _PyErr_Clear(tstate);
}
goto error; /* namespace 2: builtins */
}
Py_INCREF(v);
}
else {
v = PyObject_GetItem(BUILTINS(), name); v = PyObject_GetItem(BUILTINS(), name);
if (v == NULL) { if (v == NULL) {
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
@ -1224,7 +1230,7 @@ dummy_func(
tstate, PyExc_NameError, tstate, PyExc_NameError,
NAME_ERROR_MSG, name); NAME_ERROR_MSG, name);
} }
goto error; ERROR_IF(true, error);
} }
} }
} }

File diff suppressed because it is too large Load diff