Skip to content

Commit ec1876c

Browse files
More fixes for 3.14 and 3.15 (#602)
1 parent e89d789 commit ec1876c

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-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: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4402,6 +4402,39 @@ class Cat(Animal):
44024402
'voice': str,
44034403
}
44044404

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

7050+
@skipUnless(sys.version_info <= (3, 15), "Behavior removed in 3.15")
70177051
def test_namedtuple_keyword_usage(self):
70187052
with self.assertWarnsRegex(
70197053
DeprecationWarning,
@@ -7049,6 +7083,7 @@ def test_namedtuple_keyword_usage(self):
70497083
):
70507084
NamedTuple('Name', None, x=int)
70517085

7086+
@skipUnless(sys.version_info <= (3, 15), "Behavior removed in 3.15")
70527087
def test_namedtuple_special_keyword_names(self):
70537088
with self.assertWarnsRegex(
70547089
DeprecationWarning,
@@ -7064,6 +7099,7 @@ def test_namedtuple_special_keyword_names(self):
70647099
self.assertEqual(a.typename, 'foo')
70657100
self.assertEqual(a.fields, [('bar', tuple)])
70667101

7102+
@skipUnless(sys.version_info <= (3, 15), "Behavior removed in 3.15")
70677103
def test_empty_namedtuple(self):
70687104
expected_warning = re.escape(
70697105
"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)