Skip to content

Commit 0db2bee

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

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
@@ -554,6 +554,7 @@
554554
# mapping unique short aliases to a base URL and a prefix.
555555
# https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
556556
extlinks = {
557+
"oss-fuzz": ("https://issues.oss-fuzz.com/issues/%s", "#%s"),
557558
"pypi": ("https://pypi.org/project/%s/", "%s"),
558559
"source": (SOURCE_URI, "%s"),
559560
}

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
@@ -806,6 +806,8 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
806806
PyObject *k, *v;
807807
Py_ssize_t pos = 0;
808808
int remove_dunder_class = 0;
809+
int remove_dunder_classdict = 0;
810+
int remove_dunder_cond_annotations = 0;
809811

810812
while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) {
811813
// skip comprehension parameter
@@ -828,15 +830,27 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
828830
if (existing == NULL && PyErr_Occurred()) {
829831
return 0;
830832
}
831-
// __class__ is never allowed to be free through a class scope (see
833+
// __class__, __classdict__ and __conditional_annotations__ are
834+
// never allowed to be free through a class scope (see
832835
// drop_class_free)
833836
if (scope == FREE && ste->ste_type == ClassBlock &&
834-
_PyUnicode_EqualToASCIIString(k, "__class__")) {
837+
(_PyUnicode_EqualToASCIIString(k, "__class__") ||
838+
_PyUnicode_EqualToASCIIString(k, "__classdict__") ||
839+
_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__"))) {
835840
scope = GLOBAL_IMPLICIT;
836841
if (PySet_Discard(comp_free, k) < 0) {
837842
return 0;
838843
}
839-
remove_dunder_class = 1;
844+
845+
if (_PyUnicode_EqualToASCIIString(k, "__class__")) {
846+
remove_dunder_class = 1;
847+
}
848+
else if (_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__")) {
849+
remove_dunder_cond_annotations = 1;
850+
}
851+
else {
852+
remove_dunder_classdict = 1;
853+
}
840854
}
841855
if (!existing) {
842856
// name does not exist in scope, copy from comprehension
@@ -876,6 +890,12 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
876890
if (remove_dunder_class && PyDict_DelItemString(comp->ste_symbols, "__class__") < 0) {
877891
return 0;
878892
}
893+
if (remove_dunder_classdict && PyDict_DelItemString(comp->ste_symbols, "__classdict__") < 0) {
894+
return 0;
895+
}
896+
if (remove_dunder_cond_annotations && PyDict_DelItemString(comp->ste_symbols, "__conditional_annotations__") < 0) {
897+
return 0;
898+
}
879899
return 1;
880900
}
881901

0 commit comments

Comments
 (0)