https://github.com/python/cpython/commit/63eaaf95999c530cbd75b3addc3e660499d3adbe
commit: 63eaaf95999c530cbd75b3addc3e660499d3adbe
branch: main
author: Stan Ulbrych <[email protected]>
committer: gpshead <[email protected]>
date: 2026-03-09T12:56:41-07:00
summary:

gh-145701: Fix `__classdict__` & `__conditional_annotations__` in class-scope 
inlined comprehensions (GH-145702)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-03-09-18-52-03.gh-issue-145701.79KQyO.rst
M Lib/test/test_listcomps.py
M Python/symtable.c

diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py
index 70148dc30fc957..cee528722b85aa 100644
--- a/Lib/test/test_listcomps.py
+++ b/Lib/test/test_listcomps.py
@@ -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
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-09-18-52-03.gh-issue-145701.79KQyO.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-09-18-52-03.gh-issue-145701.79KQyO.rst
new file mode 100644
index 00000000000000..23796082fb616f
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-09-18-52-03.gh-issue-145701.79KQyO.rst
@@ -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`.
diff --git a/Python/symtable.c b/Python/symtable.c
index beb6df88d097e3..4b695e4b2588d8 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -807,6 +807,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
@@ -829,15 +831,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
@@ -877,6 +891,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;
 }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to