8000 gh-133701: Fix incorrect `__annotations__` on TypedDict defined under… · python/cpython@9836503 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9836503

Browse files
gh-133701: Fix incorrect __annotations__ on TypedDict defined under PEP 563 (#133772)
1 parent 5d118d0 commit 9836503

File tree

4 files changed

+41
-4
lines changed

4 files changed

+41
-4
lines changed

Lib/test/support/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,9 +696,11 @@ def sortdict(dict):
696696
return "{%s}" % withcommas
697697

698698

699-
def run_code(code: str) -> dict[str, object]:
699+
def run_code(code: str, extra_names: dict[str, object] | None = None) -> dict[str, object]:
700700
"""Run a piece of code after dedenting it, and return its global namespace."""
701701
ns = {}
702+
if extra_names:
703+
ns.update(extra_names)
702704
exec(textwrap.dedent(code), ns)
703705
return ns
704706

Lib/test/test_typing.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8487,6 +8487,36 @@ class Child(Base1, Base2):
84878487
self.assertEqual(Child.__required_keys__, frozenset(['a']))
84888488
self.assertEqual(Child.__optional_keys__, frozenset())
84898489

8490+
def test_inheritance_pep563(self):
8491+
def _make_td(future, class_name, annos, base, extra_names=None):
8492+
lines = []
8493+
if future:
8494+
lines.append('from __future__ import annotations')
8495+
lines.append('from typing import TypedDict')
8496+
lines.append(f'class {class_name}({base}):')
8497+
for name, anno in annos.items():
8498+
lines.append(f' {name}: {anno}')
8499+
code = '\n'.join(lines)
8500+
ns = run_code(code, extra_names)
8501+
return ns[class_name]
8502+
8503+
for base_future in (True, False):
8504+
for child_future in (True, False):
8505+
with self.subTest(base_future=base_future, child_future=child_future):
8506+
base = _make_td(
8507+
base_future, "Base", {"base": "int"}, "TypedDict"
8508+
)
8509+
self.assertIsNotNone(base.__annotate__)
8510+
child = _make_td(
8511+
child_future, "Child", {"child": "int"}, "Base", {"Base": base}
8512+
)
8513+
base_anno = ForwardRef("int", module="builtins") if base_future else int
8514+
child_anno = ForwardRef("int", module="builtins") if child_future else int
8515+
self.assertEqual(base.__annotations__, {'base': base_anno})
8516+
self.assertEqual(
8517+
child.__annotations__, {'child': child_anno, 'base': base_anno}
8518+
)
8519+
84908520
def test_required_notrequired_keys(self):
84918521
self.assertEqual(NontotalMovie.__required_keys__,
84928522
frozenset({"title"}))

Lib/typing.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3048,14 +3048,16 @@ def __new__(cls, name, bases, ns, total=True):
30483048
else:
30493049
generic_base = ()
30503050

3051+
ns_annotations = ns.pop('__annotations__', None)
3052+
30513053
tp_dict = type.__new__(_TypedDictMeta, name, (*generic_base, dict), ns)
30523054

30533055
if not hasattr(tp_dict, '__orig_bases__'):
30543056
tp_dict.__orig_bases__ = bases
30553057

3056-
if "__annotations__" in ns:
3058+
if ns_annotations is not None:
30573059
own_annotate = None
3058-
own_annotations = ns["__annotations__"]
3060+
own_annotations = ns_annotations
30593061
elif (own_annotate := _lazy_annotationlib.get_annotate_from_class_namespace(ns)) is not None:
30603062
own_annotations = _lazy_annotationlib.call_annotate_function(
30613063
own_annotate, _lazy_annotationlib.Format.FORWARDREF, owner=tp_dict
@@ -3126,7 +3128,7 @@ def __annotate__(format):
31263128
if base_annotate is None:
31273129
continue
31283130
base_annos = _lazy_annotationlib.call_annotate_function(
3129-
base.__annotate__, format, owner=base)
3131+
base_annotate, format, owner=base)
31303132
annos.update(base_annos)
31313133
if own_annotate is not None:
31323134
own = _lazy_annotationlib.call_annotate_function(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix bug where :class:`typing.TypedDict` classes defined under ``from
2+
__future__ import annotations`` and inheriting from another ``TypedDict``
3+
had an incorrect ``__annotations__`` attribute.

0 commit comments

Comments
 (0)
0