mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
[3.12] gh-119666: fix multiple class-scope comprehensions referencing __class__ (GH-120295) (#120300)
This commit is contained in:
parent
b134f47574
commit
82c93ea55a
3 changed files with 36 additions and 13 deletions
|
@ -168,6 +168,31 @@ class ListComprehensionTest(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
self._check_in_scopes(code, raises=NameError)
|
self._check_in_scopes(code, raises=NameError)
|
||||||
|
|
||||||
|
def test_references___class___defined(self):
|
||||||
|
code = """
|
||||||
|
__class__ = 2
|
||||||
|
res = [__class__ for x in [1]]
|
||||||
|
"""
|
||||||
|
self._check_in_scopes(
|
||||||
|
code, outputs={"res": [2]}, scopes=["module", "function"])
|
||||||
|
self._check_in_scopes(code, raises=NameError, scopes=["class"])
|
||||||
|
|
||||||
|
def test_references___class___enclosing(self):
|
||||||
|
code = """
|
||||||
|
__class__ = 2
|
||||||
|
class C:
|
||||||
|
res = [__class__ for x in [1]]
|
||||||
|
res = C.res
|
||||||
|
"""
|
||||||
|
self._check_in_scopes(code, raises=NameError)
|
||||||
|
|
||||||
|
def test_super_and_class_cell_in_sibling_comps(self):
|
||||||
|
code = """
|
||||||
|
[super for _ in [1]]
|
||||||
|
[__class__ for _ in [1]]
|
||||||
|
"""
|
||||||
|
self._check_in_scopes(code, raises=NameError)
|
||||||
|
|
||||||
def test_inner_cell_shadows_outer(self):
|
def test_inner_cell_shadows_outer(self):
|
||||||
code = """
|
code = """
|
||||||
items = [(lambda: i) for i in range(5)]
|
items = [(lambda: i) for i in range(5)]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix a compiler crash in the case where two comprehensions in class scope both reference ``__class__``.
|
|
@ -675,22 +675,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
|
||||||
if (existing == NULL && PyErr_Occurred()) {
|
if (existing == NULL && PyErr_Occurred()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// __class__ is never allowed to be free through a class scope (see
|
||||||
|
// drop_class_free)
|
||||||
|
if (scope == FREE && ste->ste_type == ClassBlock &&
|
||||||
|
_PyUnicode_EqualToASCIIString(k, "__class__")) {
|
||||||
|
scope = GLOBAL_IMPLICIT;
|
||||||
|
if (PySet_Discard(comp_free, k) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
remove_dunder_class = 1;
|
||||||
|
}
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
// name does not exist in scope, copy from comprehension
|
// name does not exist in scope, copy from comprehension
|
||||||
assert(scope != FREE || PySet_Contains(comp_free, k) == 1);
|
assert(scope != FREE || PySet_Contains(comp_free, k) == 1);
|
||||||
if (scope == FREE && ste->ste_type == ClassBlock &&
|
|
||||||
_PyUnicode_EqualToASCIIString(k, "__class__")) {
|
|
||||||
// if __class__ is unbound in the enclosing class scope and free
|
|
||||||
// in the comprehension scope, it needs special handling; just
|
|
||||||
// letting it be marked as free in class scope will break due to
|
|
||||||
// drop_class_free
|
|
||||||
scope = GLOBAL_IMPLICIT;
|
|
||||||
only_flags &= ~DEF_FREE;
|
|
||||||
if (PySet_Discard(comp_free, k) < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
remove_dunder_class = 1;
|
|
||||||
}
|
|
||||||
PyObject *v_flags = PyLong_FromLong(only_flags);
|
PyObject *v_flags = PyLong_FromLong(only_flags);
|
||||||
if (v_flags == NULL) {
|
if (v_flags == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue