Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fedbf77

Browse filesBrowse files
authored
gh-114828: Fix __class__ in class-scope inlined comprehensions (#115139)
1 parent 8a3c499 commit fedbf77
Copy full SHA for fedbf77

File tree

Expand file treeCollapse file tree

3 files changed

+33
-0
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+33
-0
lines changed

‎Lib/test/test_listcomps.py

Copy file name to clipboardExpand all lines: Lib/test/test_listcomps.py
+12Lines 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)]
+2Lines 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

Copy file name to clipboardExpand all lines: Python/symtable.c
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,8 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
758758
{
759759
PyObject *k, *v;
760760
Py_ssize_t pos = 0;
761+
int remove_dunder_class = 0;
762+
761763
while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) {
762764
// skip comprehension parameter
763765
long comp_flags = PyLong_AS_LONG(v);
@@ -779,6 +781,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
779781
if (!existing) {
780782
// name does not exist in scope, copy from comprehension
781783
assert(scope != FREE || PySet_Contains(comp_free, k) == 1);
784+
if (scope == FREE && ste->ste_type == ClassBlock &&
785+
_PyUnicode_EqualToASCIIString(k, "__class__")) {
786+
// if __class__ is unbound in the enclosing class scope and free
787+
// in the comprehension scope, it needs special handling; just
788+
// letting it be marked as free in class scope will break due to
789+
// drop_class_free
790+
scope = GLOBAL_IMPLICIT;
791+
only_flags &= ~DEF_FREE;
792+
if (PySet_Discard(comp_free, k) < 0) {
793+
return 0;
794+
}
795+
remove_dunder_class = 1;
796+
}
782797
PyObject *v_flags = PyLong_FromLong(only_flags);
783798
if (v_flags == NULL) {
784799
return 0;
@@ -803,6 +818,10 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
803818
}
804819
}
805820
}
821+
comp->ste_free = PySet_Size(comp_free) > 0;
822+
if (remove_dunder_class && PyDict_DelItemString(comp->ste_symbols, "__class__") < 0) {
823+
return 0;
824+
}
806825
return 1;
807826
}
808827

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.