Skip to content

Commit

Permalink
pythongh-114828: Fix __class__ in class-scope inlined comprehensions (p…
Browse files Browse the repository at this point in the history
  • Loading branch information
carljm authored Feb 7, 2024
1 parent 8a3c499 commit fedbf77
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 0 deletions.
12 changes: 12 additions & 0 deletions Lib/test/test_listcomps.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ def method(self):
self.assertEqual(C.y, [4, 4, 4, 4, 4])
self.assertIs(C().method(), C)

def test_references_super(self):
code = """
res = [super for x in [1]]
"""
self._check_in_scopes(code, outputs={"res": [super]})

def test_references___class__(self):
code = """
res = [__class__ for x 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,2 @@
Fix compilation crashes in uncommon code examples using :func:`super` inside
a comprehension in a class body.
19 changes: 19 additions & 0 deletions Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,8 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
{
PyObject *k, *v;
Py_ssize_t pos = 0;
int remove_dunder_class = 0;

while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) {
// skip comprehension parameter
long comp_flags = PyLong_AS_LONG(v);
Expand All @@ -779,6 +781,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
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 All @@ -803,6 +818,10 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
}
}
}
comp->ste_free = PySet_Size(comp_free) > 0;
if (remove_dunder_class && PyDict_DelItemString(comp->ste_symbols, "__class__") < 0) {
return 0;
}
return 1;
}

Expand Down

0 comments on commit fedbf77

Please sign in to comment.