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 d6cb8fa

Browse filesBrowse files
[3.14] gh-133701: Fix incorrect __annotations__ on TypedDict defined under PEP 563 (GH-133772) (#134003)
gh-133701: Fix incorrect `__annotations__` on TypedDict defined under PEP 563 (GH-133772) (cherry picked from commit 9836503) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
1 parent a962934 commit d6cb8fa
Copy full SHA for d6cb8fa

File tree

4 files changed

+41
-4
lines changed
Filter options

4 files changed

+41
-4
lines changed

‎Lib/test/support/__init__.py

Copy file name to clipboardExpand all lines: Lib/test/support/__init__.py
+3-1Lines 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

Copy file name to clipboardExpand all lines: Lib/test/test_typing.py
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8538,6 +8538,36 @@ class Child(Base1, Base2):
85388538
self.assertEqual(Child.__required_keys__, frozenset(['a']))
85398539
self.assertEqual(Child.__optional_keys__, frozenset())
85408540

8541+
def test_inheritance_pep563(self):
8542+
def _make_td(future, class_name, annos, base, extra_names=None):
8543+
lines = []
8544+
if future:
8545+
lines.append('from __future__ import annotations')
8546+
lines.append('from typing import TypedDict')
8547+
lines.append(f'class {class_name}({base}):')
8548+
for name, anno in annos.items():
8549+
lines.append(f' {name}: {anno}')
8550+
code = '\n'.join(lines)
8551+
ns = run_code(code, extra_names)
8552+
return ns[class_name]
8553+
8554+
for base_future in (True, False):
8555+
for child_future in (True, False):
8556+
with self.subTest(base_future=base_future, child_future=child_future):
8557+
base = _make_td(
8558+
base_future, "Base", {"base": "int"}, "TypedDict"
8559+
)
8560+
self.assertIsNotNone(base.__annotate__)
8561+
child = _make_td(
8562+
child_future, "Child", {"child": "int"}, "Base", {"Base": base}
8563+
)
8564+
base_anno = ForwardRef("int", module="builtins") if base_future else int
8565+
child_anno = ForwardRef("int", module="builtins") if child_future else int
8566+
self.assertEqual(base.__annotations__, {'base': base_anno})
8567+
self.assertEqual(
8568+
child.__annotations__, {'child': child_anno, 'base': base_anno}
8569+
)
8570+
85418571
def test_required_notrequired_keys(self):
85428572
self.assertEqual(NontotalMovie.__required_keys__,
85438573
frozenset({"title"}))

‎Lib/typing.py

Copy file name to clipboardExpand all lines: Lib/typing.py
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3087,14 +3087,16 @@ def __new__(cls, name, bases, ns, total=True):
30873087
else:
30883088
generic_base = ()
30893089

3090+
ns_annotations = ns.pop('__annotations__', None)
3091+
30903092
tp_dict = type.__new__(_TypedDictMeta, name, (*generic_base, dict), ns)
30913093

30923094
if not hasattr(tp_dict, '__orig_bases__'):
30933095
tp_dict.__orig_bases__ = bases
30943096

3095-
if "__annotations__" in ns:
3097+
if ns_annotations is not None:
30963098
own_annotate = None
3097-
own_annotations = ns["__annotations__"]
3099+
own_annotations = ns_annotations
30983100
elif (own_annotate := _lazy_annotationlib.get_annotate_from_class_namespace(ns)) is not None:
30993101
own_annotations = _lazy_annotationlib.call_annotate_function(
31003102
own_annotate, _lazy_annotationlib.Format.FORWARDREF, owner=tp_dict
@@ -3165,7 +3167,7 @@ def __annotate__(format):
31653167
if base_annotate is None:
31663168
continue
31673169
base_annos = _lazy_annotationlib.call_annotate_function(
3168-
base.__annotate__, format, owner=base)
3170+
base_annotate, format, owner=base)
31693171
annos.update(base_annos)
31703172
if own_annotate is not None:
31713173
own = _lazy_annotationlib.call_annotate_function(
+3Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.