Skip to content

Commit 321daab

Browse files
[3.13] gh-145701: Fix __classdict__ & __conditional_annotations__ in class-scope inlined comprehensions (GH-145702) (#145711)
* gh-145701: Fix `__classdict__` & `__conditional_annotations__` in class-scope inlined comprehensions (GH-145702) (cherry picked from commit 63eaaf9) Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> * Add `:oss-fuzz:` macro support Backports part of 255e79f. --------- Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
1 parent 4800aa3 commit 321daab

File tree

4 files changed

+39
-3
lines changed

4 files changed

+39
-3
lines changed

Doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@
547547
# mapping unique short aliases to a base URL and a prefix.
548548
# https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
549549
extlinks = {
550+
"oss-fuzz": ("https://issues.oss-fuzz.com/issues/%s", "#%s"),
550551
"pypi": ("https://pypi.org/project/%s/", "%s"),
551552
"source": (SOURCE_URI, "%s"),
552553
}

Lib/test/test_listcomps.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ def test_references___class___defined(self):
180180
code, outputs={"res": [2]}, scopes=["module", "function"])
181181
self._check_in_scopes(code, raises=NameError, scopes=["class"])
182182

183+
def test_references___classdict__(self):
184+
code = """
185+
class i: [__classdict__ for x in y]
186+
"""
187+
self._check_in_scopes(code, raises=NameError)
188+
189+
def test_references___conditional_annotations__(self):
190+
code = """
191+
class i: [__conditional_annotations__ for x in y]
192+
"""
193+
self._check_in_scopes(code, raises=NameError)
194+
183195
def test_references___class___enclosing(self):
184196
code = """
185197
__class__ = 2
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :exc:`SystemError` when ``__classdict__`` or
2+
``__conditional_annotations__`` is in a class-scope inlined comprehension.
3+
Found by OSS Fuzz in :oss-fuzz:`491105000`.

Python/symtable.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,8 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
763763
PyObject *k, *v;
764764
Py_ssize_t pos = 0;
765765
int remove_dunder_class = 0;
766+
int remove_dunder_classdict = 0;
767+
int remove_dunder_cond_annotations = 0;
766768

767769
while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) {
768770
// skip comprehension parameter
@@ -782,15 +784,27 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
782784
if (existing == NULL && PyErr_Occurred()) {
783785
return 0;
784786
}
785-
// __class__ is never allowed to be free through a class scope (see
787+
// __class__, __classdict__ and __conditional_annotations__ are
788+
// never allowed to be free through a class scope (see
786789
// drop_class_free)
787790
if (scope == FREE && ste->ste_type == ClassBlock &&
788-
_PyUnicode_EqualToASCIIString(k, "__class__")) {
791+
(_PyUnicode_EqualToASCIIString(k, "__class__") ||
792+
_PyUnicode_EqualToASCIIString(k, "__classdict__") ||
793+
_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__"))) {
789794
scope = GLOBAL_IMPLICIT;
790795
if (PySet_Discard(comp_free, k) < 0) {
791796
return 0;
792797
}
793-
remove_dunder_class = 1;
798+
799+
if (_PyUnicode_EqualToASCIIString(k, "__class__")) {
800+
remove_dunder_class = 1;
801+
}
802+
else if (_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__")) {
803+
remove_dunder_cond_annotations = 1;
804+
}
805+
else {
806+
remove_dunder_classdict = 1;
807+
}
794808
}
795809
if (!existing) {
796810
// name does not exist in scope, copy from comprehension
@@ -823,6 +837,12 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
823837
if (remove_dunder_class && PyDict_DelItemString(comp->ste_symbols, "__class__") < 0) {
824838
return 0;
825839
}
840+
if (remove_dunder_classdict && PyDict_DelItemString(comp->ste_symbols, "__classdict__") < 0) {
841+
return 0;
842+
}
843+
if (remove_dunder_cond_annotations && PyDict_DelItemString(comp->ste_symbols, "__conditional_annotations__") < 0) {
844+
return 0;
845+
}
826846
return 1;
827847
}
828848

0 commit comments

Comments
 (0)