Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 967f27d

Browse files
[3.12] gh-114828: Fix __class__ in class-scope inlined comprehensions (GH-115139) (#115140)
gh-114828: Fix __class__ in class-scope inlined comprehensions (GH-115139) (cherry picked from commit fedbf77) Co-authored-by: Carl Meyer <[email protected]>
1 parent a69e68d commit 967f27d

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

Lib/test/test_listcomps.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@ def method(self):
156156
self.assertEqual(C.y, [4, 4, 4, 4, 4])
157157
self.assertIs(C().method(), C)
158158

159+
def test_references_super(self):
160+
code = """
161+
res = [super for x in [1]]
162+
"""
163+
self._check_in_scopes(code, outputs={"res": [super]})
164+
165+
def test_references___class__(self):
166+
code = """
167+
res = [__class__ for x in [1]]
168+
"""
169+
self._check_in_scopes(code, raises=NameError)
170+
159171
def test_inner_cell_shadows_outer(self):
160172
code = """
161173
items = [(lambda: i) for i in range(5)]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix compilation crashes in uncommon code examples using :func:`super` inside
2+
a comprehension in a class body.

Python/symtable.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,8 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
658658
{
659659
PyObject *k, *v;
660660
Py_ssize_t pos = 0;
661+
int remove_dunder_class = 0;
662+
661663
while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) {
662664
// skip comprehension parameter
663665
long comp_flags = PyLong_AS_LONG(v);
@@ -679,6 +681,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
679681
if (!existing) {
680682
// name does not exist in scope, copy from comprehension
681683
assert(scope != FREE || PySet_Contains(comp_free, k) == 1);
684+
if (scope == FREE && ste->ste_type == ClassBlock &&
685+
_PyUnicode_EqualToASCIIString(k, "__class__")) {
686+
// if __class__ is unbound in the enclosing class scope and free
687+
// in the comprehension scope, it needs special handling; just
688+
// letting it be marked as free in class scope will break due to
689+
// drop_class_free
690+
scope = GLOBAL_IMPLICIT;
691+
only_flags &= ~DEF_FREE;
692+
if (PySet_Discard(comp_free, k) < 0) {
693+
return 0;
694+
}
695+
remove_dunder_class = 1;
696+
}
682697
PyObject *v_flags = PyLong_FromLong(only_flags);
683698
if (v_flags == NULL) {
684699
return 0;
@@ -703,6 +718,10 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
703718
}
704719
}
705720
}
721+
comp->ste_free = PySet_Size(comp_free) > 0;
722+
if (remove_dunder_class && PyDict_DelItemString(comp->ste_symbols, "__class__") < 0) {
723+
return 0;
724+
}
706725
return 1;
707726
}
708727

0 commit comments

Comments
 (0)