Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 667cf50

Browse files
carljmpull[bot]
authored andcommitted
pythongh-119666: fix multiple class-scope comprehensions referencing __class__ (python#120295)
1 parent afd2a6d commit 667cf50

File tree

3 files changed

+36
-13
lines changed

3 files changed

+36
-13
lines changed

Lib/test/test_listcomps.py

+25
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,31 @@ def test_references___class__(self):
168168
"""
169169
self._check_in_scopes(code, raises=NameError)
170170

171+
def test_references___class___defined(self):
172+
code = """
173+
__class__ = 2
174+
res = [__class__ for x in [1]]
175+
"""
176+
self._check_in_scopes(
177+
code, outputs={"res": [2]}, scopes=["module", "function"])
178+
self._check_in_scopes(code, raises=NameError, scopes=["class"])
179+
180+
def test_references___class___enclosing(self):
181+
code = """
182+
__class__ = 2
183+
class C:
184+
res = [__class__ for x in [1]]
185+
res = C.res
186+
"""
187+
self._check_in_scopes(code, raises=NameError)
188+
189+
def test_super_and_class_cell_in_sibling_comps(self):
190+
code = """
191+
[super for _ in [1]]
192+
[__class__ for _ in [1]]
193+
"""
194+
self._check_in_scopes(code, raises=NameError)
195+
171196
def test_inner_cell_shadows_outer(self):
172197
code = """
173198
items = [(lambda: i) for i in range(5)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a compiler crash in the case where two comprehensions in class scope both reference ``__class__``.

Python/symtable.c

+10-13
Original file line numberDiff line numberDiff line change
@@ -781,22 +781,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
781781
if (existing == NULL && PyErr_Occurred()) {
782782
return 0;
783783
}
784+
// __class__ is never allowed to be free through a class scope (see
785+
// drop_class_free)
786+
if (scope == FREE && ste->ste_type == ClassBlock &&
787+
_PyUnicode_EqualToASCIIString(k, "__class__")) {
788+
scope = GLOBAL_IMPLICIT;
789+
if (PySet_Discard(comp_free, k) < 0) {
790+
return 0;
791+
}
792+
remove_dunder_class = 1;
793+
}
784794
if (!existing) {
785795
// name does not exist in scope, copy from comprehension
786796
assert(scope != FREE || PySet_Contains(comp_free, k) == 1);
787-
if (scope == FREE && ste->ste_type == ClassBlock &&
788-
_PyUnicode_EqualToASCIIString(k, "__class__")) {
789-
// if __class__ is unbound in the enclosing class scope and free
790-
// in the comprehension scope, it needs special handling; just
791-
// letting it be marked as free in class scope will break due to
792-
// drop_class_free
793-
scope = GLOBAL_IMPLICIT;
794-
only_flags &= ~DEF_FREE;
795-
if (PySet_Discard(comp_free, k) < 0) {
796-
return 0;
797-
}
798-
remove_dunder_class = 1;
799-
}
800797
PyObject *v_flags = PyLong_FromLong(only_flags);
801798
if (v_flags == NULL) {
802799
return 0;

0 commit comments

Comments
 (0)