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]