Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
26 changes: 26 additions & 0 deletions Lib/test/test_type_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,32 @@ class C:
""")
self.assertEqual(ns["C"].__annotations__, {"__classdict__": int})

def test_class_binds_dunder_classdict(self):
# The class body binding __classdict__ must not hide the cell that
# annotation and type parameter scopes use to see the class namespace.
for binding in (
"__classdict__ = 1",
"__classdict__: str = 1",
"for __classdict__ in (1,): pass",
"global __classdict__",
):
with self.subTest(binding=binding):
ns = run_code(f"""
class C:
{binding}
y = int
def f(x: y): pass
class D[T: y]: pass
class E[T]: pass
type A[T: y] = y
""")
C = ns["C"]
self.assertEqual(C.f.__annotations__, {"x": int})
self.assertIs(C.D.__type_params__[0].__bound__, int)
self.assertEqual(C.E.__type_params__[0].__name__, "T")
self.assertIs(C.A.__type_params__[0].__bound__, int)
self.assertIs(C.A.__value__, int)

def test_future_annotations(self):
code = """
from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a crash and a :exc:`SystemError` when compiling a class whose body binds
``__classdict__`` (for example ``__classdict__ = 1`` or ``global
__classdict__``) and also contains an annotated function, a generic class or
function, or a generic type alias.
4 changes: 3 additions & 1 deletion Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,9 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
// namespace and the globals (not the namespace providing the bound).
// Similarly, if the name is explicitly global in the class namespace (through the
// global statement), we want to also treat it as a global in this scope.
if (class_entry != NULL) {
// __classdict__ is the exception: these scopes load it from the cell that
// the class creates for them, whatever the class body does with that name.
if (class_entry != NULL && !_PyUnicode_Equal(name, &_Py_ID(__classdict__))) {
long class_flags = _PyST_GetSymbol(class_entry, name);
if (class_flags < 0) {
return 0;
Expand Down
Loading