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

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-145701: Fix __classdict__ & __conditional_annotations__ in cla…
…ss-scope inlined comprehensions (GH-145702)

(cherry picked from commit 63eaaf9)

Co-authored-by: Stan Ulbrych <[email protected]>
  • Loading branch information
StanFromIreland authored and miss-islington committed Mar 9, 2026
commit 00b513a17316092135b8a4b37608d76b8c9b3bd3
12 changes: 12 additions & 0 deletions Lib/test/test_listcomps.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,18 @@ def test_references___class___defined(self):
code, outputs={"res": [2]}, scopes=["module", "function"])
self._check_in_scopes(code, raises=NameError, scopes=["class"])

def test_references___classdict__(self):
code = """
class i: [__classdict__ for x in y]
"""
self._check_in_scopes(code, raises=NameError)

def test_references___conditional_annotations__(self):
code = """
class i: [__conditional_annotations__ for x in y]
"""
self._check_in_scopes(code, raises=NameError)

def test_references___class___enclosing(self):
code = """
__class__ = 2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :exc:`SystemError` when ``__classdict__`` or
``__conditional_annotations__`` is in a class-scope inlined comprehension.
Found by OSS Fuzz in :oss-fuzz:`491105000`.
26 changes: 23 additions & 3 deletions Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,8 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
PyObject *k, *v;
Py_ssize_t pos = 0;
int remove_dunder_class = 0;
int remove_dunder_classdict = 0;
int remove_dunder_cond_annotations = 0;

while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) {
// skip comprehension parameter
Expand All @@ -828,15 +830,27 @@ 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
// __class__, __classdict__ and __conditional_annotations__ are
// never allowed to be free through a class scope (see
// drop_class_free)
if (scope == FREE && ste->ste_type == ClassBlock &&
_PyUnicode_EqualToASCIIString(k, "__class__")) {
(_PyUnicode_EqualToASCIIString(k, "__class__") ||
_PyUnicode_EqualToASCIIString(k, "__classdict__") ||
_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__"))) {
scope = GLOBAL_IMPLICIT;
if (PySet_Discard(comp_free, k) < 0) {
return 0;
}
remove_dunder_class = 1;

if (_PyUnicode_EqualToASCIIString(k, "__class__")) {
remove_dunder_class = 1;
}
else if (_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__")) {
remove_dunder_cond_annotations = 1;
}
else {
remove_dunder_classdict = 1;
}
}
if (!existing) {
// name does not exist in scope, copy from comprehension
Expand Down Expand Up @@ -876,6 +890,12 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
if (remove_dunder_class && PyDict_DelItemString(comp->ste_symbols, "__class__") < 0) {
return 0;
}
if (remove_dunder_classdict && PyDict_DelItemString(comp->ste_symbols, "__classdict__") < 0) {
return 0;
}
if (remove_dunder_cond_annotations && PyDict_DelItemString(comp->ste_symbols, "__conditional_annotations__") < 0) {
return 0;
}
return 1;
}

Expand Down
Loading