Skip to content

Commit df994ab

Browse files
committed
More fixes for 3.14 and 3.15
1 parent e89d789 commit df994ab

File tree

3 files changed

+44
-6
lines changed

3 files changed

+44
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
- Do not attempt to re-export names that have been removed from `typing`,
55
anticipating the removal of `typing.no_type_check_decorator` in Python 3.15.
66
Patch by Jelle Zijlstra.
7-
- Update `typing_extensions.Format` and `typing_extensions.evaluate_forward_ref` to align
8-
with changes in Python 3.14. Patch by Jelle Zijlstra.
9-
- Fix tests for Python 3.14. Patch by Jelle Zijlstra.
7+
- Update `typing_extensions.Format`, `typing_extensions.evaluate_forward_ref`, and
8+
`typing_extensions.TypedDict` to align
9+
with changes in Python 3.14. Patches by Jelle Zijlstra.
10+
- Fix tests for Python 3.14 and 3.15. Patches by Jelle Zijlstra.
1011

1112
New features:
1213

src/test_typing_extensions.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4402,6 +4402,38 @@ class Cat(Animal):
44024402
'voice': str,
44034403
}
44044404

4405+
def test_inheritance_pep563(self):
4406+
def _make_td(future, class_name, annos, base, extra_names=None):
4407+
lines = []
4408+
if future:
4409+
lines.append('from __future__ import annotations')
4410+
lines.append('from typing import TypedDict')
4411+
lines.append(f'class {class_name}({base}):')
4412+
for name, anno in annos.items():
4413+
lines.append(f' {name}: {anno}')
4414+
code = '\n'.join(lines)
4415+
ns = {**extra_names} if extra_names else {}
4416+
exec(code, ns)
4417+
return ns[class_name]
4418+
4419+
for base_future in (True, False):
4420+
for child_future in (True, False):
4421+
with self.subTest(base_future=base_future, child_future=child_future):
4422+
base = _make_td(
4423+
base_future, "Base", {"base": "int"}, "TypedDict"
4424+
)
4425+
if sys.version_info >= (3, 14):
4426+
self.assertIsNotNone(base.__annotate__)
4427+
child = _make_td(
4428+
child_future, "Child", {"child": "int"}, "Base", {"Base": base}
4429+
)
4430+
base_anno = typing.ForwardRef("int", module="builtins") if base_future else int
4431+
child_anno = typing.ForwardRef("int", module="builtins") if child_future else int
4432+
self.assertEqual(base.__annotations__, {'base': base_anno})
4433+
self.assertEqual(
4434+
child.__annotations__, {'child': child_anno, 'base': base_anno}
4435+
)
4436+
44054437
def test_required_notrequired_keys(self):
44064438
self.assertEqual(NontotalMovie.__required_keys__,
44074439
frozenset({"title"}))
@@ -7014,6 +7046,7 @@ class Group(NamedTuple):
70147046
self.assertIs(type(a), Group)
70157047
self.assertEqual(a, (1, [2]))
70167048

7049+
@skipUnless(sys.version_info <= (3, 15), "Behavior removed in 3.15")
70177050
def test_namedtuple_keyword_usage(self):
70187051
with self.assertWarnsRegex(
70197052
DeprecationWarning,
@@ -7049,6 +7082,7 @@ def test_namedtuple_keyword_usage(self):
70497082
):
70507083
NamedTuple('Name', None, x=int)
70517084

7085+
@skipUnless(sys.version_info <= (3, 15), "Behavior removed in 3.15")
70527086
def test_namedtuple_special_keyword_names(self):
70537087
with self.assertWarnsRegex(
70547088
DeprecationWarning,
@@ -7064,6 +7098,7 @@ def test_namedtuple_special_keyword_names(self):
70647098
self.assertEqual(a.typename, 'foo')
70657099
self.assertEqual(a.fields, [('bar', tuple)])
70667100

7101+
@skipUnless(sys.version_info <= (3, 15), "Behavior removed in 3.15")
70677102
def test_empty_namedtuple(self):
70687103
expected_warning = re.escape(
70697104
"Failing to pass a value for the 'fields' parameter is deprecated "

src/typing_extensions.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,8 @@ def __new__(cls, name, bases, ns, *, total=True, closed=None,
10161016
else:
10171017
generic_base = ()
10181018

1019+
ns_annotations = ns.pop('__annotations__', None)
1020+
10191021
# typing.py generally doesn't let you inherit from plain Generic, unless
10201022
# the name of the class happens to be "Protocol"
10211023
tp_dict = type.__new__(_TypedDictMeta, "Protocol", (*generic_base, dict), ns)
@@ -1028,8 +1030,8 @@ def __new__(cls, name, bases, ns, *, total=True, closed=None,
10281030

10291031
annotations = {}
10301032
own_annotate = None
1031-
if "__annotations__" in ns:
1032-
own_annotations = ns["__annotations__"]
1033+
if ns_annotations is not None:
1034+
own_annotations = ns_annotations
10331035
elif sys.version_info >= (3, 14):
10341036
if hasattr(annotationlib, "get_annotate_from_class_namespace"):
10351037
own_annotate = annotationlib.get_annotate_from_class_namespace(ns)
@@ -1119,7 +1121,7 @@ def __annotate__(format):
11191121
if base_annotate is None:
11201122
continue
11211123
base_annos = annotationlib.call_annotate_function(
1122-
base.__annotate__, format, owner=base)
1124+
base_annotate, format, owner=base)
11231125
annos.update(base_annos)
11241126
if own_annotate is not None:
11251127
own = annotationlib.call_annotate_function(

0 commit comments

Comments
 (0)