Skip to content

Commit

Permalink
pythongh-119666: fix multiple class-scope comprehensions referencing …
Browse files Browse the repository at this point in the history
…__class__ (python#120295)
  • Loading branch information
carljm authored and estyxx committed Jul 17, 2024
1 parent e577551 commit 28ac828
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 13 deletions.
25 changes: 25 additions & 0 deletions Lib/test/test_listcomps.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,31 @@ def test_references___class__(self):
"""
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):
code = """
items = [(lambda: i) for i in range(5)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a compiler crash in the case where two comprehensions in class scope both reference ``__class__``.
23 changes: 10 additions & 13 deletions Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -781,22 +781,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
if (existing == NULL && PyErr_Occurred()) {
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) {
// name does not exist in scope, copy from comprehension
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);
if (v_flags == NULL) {
return 0;
Expand Down

0 comments on commit 28ac828

Please sign in to comment.