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 75c95c4

Browse filesBrowse files
Start PEP 728 implementation (#519)
1 parent 7def253 commit 75c95c4
Copy full SHA for 75c95c4

File tree

2 files changed

+249
-92
lines changed
Filter options

2 files changed

+249
-92
lines changed

‎src/test_typing_extensions.py

Copy file name to clipboardExpand all lines: src/test_typing_extensions.py
+160-51Lines changed: 160 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
Never,
5656
NewType,
5757
NoDefault,
58+
NoExtraItems,
5859
NoReturn,
5960
NotRequired,
6061
Optional,
@@ -128,6 +129,8 @@
128129
# 3.13.0.rc1 fixes a problem with @deprecated
129130
TYPING_3_13_0_RC = sys.version_info[:4] >= (3, 13, 0, "candidate")
130131

132+
TYPING_3_14_0 = sys.version_info[:3] >= (3, 14, 0)
133+
131134
# https://github.com/python/cpython/pull/27017 was backported into some 3.9 and 3.10
132135
# versions, but not all
133136
HAS_FORWARD_MODULE = "module" in inspect.signature(typing._type_check).parameters
@@ -4140,18 +4143,25 @@ def test_basics_keywords_syntax(self):
41404143
def test_typeddict_special_keyword_names(self):
41414144
with self.assertWarns(DeprecationWarning):
41424145
TD = TypedDict("TD", cls=type, self=object, typename=str, _typename=int,
4143-
fields=list, _fields=dict)
4146+
fields=list, _fields=dict,
4147+
closed=bool, extra_items=bool)
41444148
self.assertEqual(TD.__name__, 'TD')
41454149
self.assertEqual(TD.__annotations__, {'cls': type, 'self': object, 'typename': str,
4146-
'_typename': int, 'fields': list, '_fields': dict})
4150+
'_typename': int, 'fields': list, '_fields': dict,
4151+
'closed': bool, 'extra_items': bool})
4152+
self.assertIsNone(TD.__closed__)
4153+
self.assertIs(TD.__extra_items__, NoExtraItems)
41474154
a = TD(cls=str, self=42, typename='foo', _typename=53,
4148-
fields=[('bar', tuple)], _fields={'baz', set})
4155+
fields=[('bar', tuple)], _fields={'baz', set},
4156+
closed=None, extra_items="tea pot")
41494157
self.assertEqual(a['cls'], str)
41504158
self.assertEqual(a['self'], 42)
41514159
self.assertEqual(a['typename'], 'foo')
41524160
self.assertEqual(a['_typename'], 53)
41534161
self.assertEqual(a['fields'], [('bar', tuple)])
41544162
self.assertEqual(a['_fields'], {'baz', set})
4163+
self.assertIsNone(a['closed'])
4164+
self.assertEqual(a['extra_items'], "tea pot")
41554165

41564166
def test_typeddict_create_errors(self):
41574167
with self.assertRaises(TypeError):
@@ -4414,24 +4424,6 @@ class ChildWithInlineAndOptional(Untotal, Inline):
44144424
{'inline': bool, 'untotal': str, 'child': bool},
44154425
)
44164426

4417-
class Closed(TypedDict, closed=True):
4418-
__extra_items__: None
4419-
4420-
class Unclosed(TypedDict, closed=False):
4421-
...
4422-
4423-
class ChildUnclosed(Closed, Unclosed):
4424-
...
4425-
4426-
self.assertFalse(ChildUnclosed.__closed__)
4427-
self.assertEqual(ChildUnclosed.__extra_items__, type(None))
4428-
4429-
class ChildClosed(Unclosed, Closed):
4430-
...
4431-
4432-
self.assertFalse(ChildClosed.__closed__)
4433-
self.assertEqual(ChildClosed.__extra_items__, type(None))
4434-
44354427
wrong_bases = [
44364428
(One, Regular),
44374429
(Regular, One),
@@ -4448,6 +4440,53 @@ class ChildClosed(Unclosed, Closed):
44484440
class Wrong(*bases):
44494441
pass
44504442

4443+
def test_closed_values(self):
4444+
class Implicit(TypedDict): ...
4445+
class ExplicitTrue(TypedDict, closed=True): ...
4446+
class ExplicitFalse(TypedDict, closed=False): ...
4447+
4448+
self.assertIsNone(Implicit.__closed__)
4449+
self.assertIs(ExplicitTrue.__closed__, True)
4450+
self.assertIs(ExplicitFalse.__closed__, False)
4451+
4452+
4453+
@skipIf(TYPING_3_14_0, "only supported on older versions")
4454+
def test_closed_typeddict_compat(self):
4455+
class Closed(TypedDict, closed=True):
4456+
__extra_items__: None
4457+
4458+
class Unclosed(TypedDict, closed=False):
4459+
...
4460+
4461+
class ChildUnclosed(Closed, Unclosed):
4462+
...
4463+
4464+
self.assertIsNone(ChildUnclosed.__closed__)
4465+
self.assertEqual(ChildUnclosed.__extra_items__, NoExtraItems)
4466+
4467+
class ChildClosed(Unclosed, Closed):
4468+
...
4469+
4470+
self.assertIsNone(ChildClosed.__closed__)
4471+
self.assertEqual(ChildClosed.__extra_items__, NoExtraItems)
4472+
4473+
def test_extra_items_class_arg(self):
4474+
class TD(TypedDict, extra_items=int):
4475+
a: str
4476+
4477+
self.assertIs(TD.__extra_items__, int)
4478+
self.assertEqual(TD.__annotations__, {'a': str})
4479+
self.assertEqual(TD.__required_keys__, frozenset({'a'}))
4480+
self.assertEqual(TD.__optional_keys__, frozenset())
4481+
4482+
class NoExtra(TypedDict):
4483+
a: str
4484+
4485+
self.assertIs(NoExtra.__extra_items__, NoExtraItems)
4486+
self.assertEqual(NoExtra.__annotations__, {'a': str})
4487+
self.assertEqual(NoExtra.__required_keys__, frozenset({'a'}))
4488+
self.assertEqual(NoExtra.__optional_keys__, frozenset())
4489+
44514490
def test_is_typeddict(self):
44524491
self.assertIs(is_typeddict(Point2D), True)
44534492
self.assertIs(is_typeddict(Point2Dor3D), True)
@@ -4803,7 +4842,8 @@ class AllTheThings(TypedDict):
48034842
},
48044843
)
48054844

4806-
def test_extra_keys_non_readonly(self):
4845+
@skipIf(TYPING_3_14_0, "Old syntax only supported on <3.14")
4846+
def test_extra_keys_non_readonly_legacy(self):
48074847
class Base(TypedDict, closed=True):
48084848
__extra_items__: str
48094849

@@ -4815,7 +4855,8 @@ class Child(Base):
48154855
self.assertEqual(Child.__readonly_keys__, frozenset({}))
48164856
self.assertEqual(Child.__mutable_keys__, frozenset({'a'}))
48174857

4818-
def test_extra_keys_readonly(self):
4858+
@skipIf(TYPING_3_14_0, "Only supported on <3.14")
4859+
def test_extra_keys_readonly_legacy(self):
48194860
class Base(TypedDict, closed=True):
48204861
__extra_items__: ReadOnly[str]
48214862

@@ -4827,7 +4868,21 @@ class Child(Base):
48274868
self.assertEqual(Child.__readonly_keys__, frozenset({}))
48284869
self.assertEqual(Child.__mutable_keys__, frozenset({'a'}))
48294870

4830-
def test_extra_key_required(self):
4871+
@skipIf(TYPING_3_14_0, "Only supported on <3.14")
4872+
def test_extra_keys_readonly_explicit_closed_legacy(self):
4873+
class Base(TypedDict, closed=True):
4874+
__extra_items__: ReadOnly[str]
4875+
4876+
class Child(Base, closed=True):
4877+
a: NotRequired[str]
4878+
4879+
self.assertEqual(Child.__required_keys__, frozenset({}))
4880+
self.assertEqual(Child.__optional_keys__, frozenset({'a'}))
4881+
self.assertEqual(Child.__readonly_keys__, frozenset({}))
4882+
self.assertEqual(Child.__mutable_keys__, frozenset({'a'}))
4883+
4884+
@skipIf(TYPING_3_14_0, "Only supported on <3.14")
4885+
def test_extra_key_required_legacy(self):
48314886
with self.assertRaisesRegex(
48324887
TypeError,
48334888
"Special key __extra_items__ does not support Required"
@@ -4840,16 +4895,16 @@ def test_extra_key_required(self):
48404895
):
48414896
TypedDict("A", {"__extra_items__": NotRequired[int]}, closed=True)
48424897

4843-
def test_regular_extra_items(self):
4898+
def test_regular_extra_items_legacy(self):
48444899
class ExtraReadOnly(TypedDict):
48454900
__extra_items__: ReadOnly[str]
48464901

48474902
self.assertEqual(ExtraReadOnly.__required_keys__, frozenset({'__extra_items__'}))
48484903
self.assertEqual(ExtraReadOnly.__optional_keys__, frozenset({}))
48494904
self.assertEqual(ExtraReadOnly.__readonly_keys__, frozenset({'__extra_items__'}))
48504905
self.assertEqual(ExtraReadOnly.__mutable_keys__, frozenset({}))
4851-
self.assertEqual(ExtraReadOnly.__extra_items__, None)
4852-
self.assertFalse(ExtraReadOnly.__closed__)
4906+
self.assertIs(ExtraReadOnly.__extra_items__, NoExtraItems)
4907+
self.assertIsNone(ExtraReadOnly.__closed__)
48534908

48544909
class ExtraRequired(TypedDict):
48554910
__extra_items__: Required[str]
@@ -4858,8 +4913,8 @@ class ExtraRequired(TypedDict):
48584913
self.assertEqual(ExtraRequired.__optional_keys__, frozenset({}))
48594914
self.assertEqual(ExtraRequired.__readonly_keys__, frozenset({}))
48604915
self.assertEqual(ExtraRequired.__mutable_keys__, frozenset({'__extra_items__'}))
4861-
self.assertEqual(ExtraRequired.__extra_items__, None)
4862-
self.assertFalse(ExtraRequired.__closed__)
4916+
self.assertIs(ExtraRequired.__extra_items__, NoExtraItems)
4917+
self.assertIsNone(ExtraRequired.__closed__)
48634918

48644919
class ExtraNotRequired(TypedDict):
48654920
__extra_items__: NotRequired[str]
@@ -4868,10 +4923,11 @@ class ExtraNotRequired(TypedDict):
48684923
self.assertEqual(ExtraNotRequired.__optional_keys__, frozenset({'__extra_items__'}))
48694924
self.assertEqual(ExtraNotRequired.__readonly_keys__, frozenset({}))
48704925
self.assertEqual(ExtraNotRequired.__mutable_keys__, frozenset({'__extra_items__'}))
4871-
self.assertEqual(ExtraNotRequired.__extra_items__, None)
4872-
self.assertFalse(ExtraNotRequired.__closed__)
4926+
self.assertIs(ExtraNotRequired.__extra_items__, NoExtraItems)
4927+
self.assertIsNone(ExtraNotRequired.__closed__)
48734928

4874-
def test_closed_inheritance(self):
4929+
@skipIf(TYPING_3_14_0, "Only supported on <3.14")
4930+
def test_closed_inheritance_legacy(self):
48754931
class Base(TypedDict, closed=True):
48764932
__extra_items__: ReadOnly[Union[str, None]]
48774933

@@ -4881,49 +4937,97 @@ class Base(TypedDict, closed=True):
48814937
self.assertEqual(Base.__mutable_keys__, frozenset({}))
48824938
self.assertEqual(Base.__annotations__, {})
48834939
self.assertEqual(Base.__extra_items__, ReadOnly[Union[str, None]])
4884-
self.assertTrue(Base.__closed__)
4940+
self.assertIs(Base.__closed__, True)
48854941

4886-
class Child(Base):
4942+
class Child(Base, closed=True):
48874943
a: int
48884944
__extra_items__: int
48894945

4890-
self.assertEqual(Child.__required_keys__, frozenset({'a', "__extra_items__"}))
4946+
self.assertEqual(Child.__required_keys__, frozenset({'a'}))
48914947
self.assertEqual(Child.__optional_keys__, frozenset({}))
48924948
self.assertEqual(Child.__readonly_keys__, frozenset({}))
4893-
self.assertEqual(Child.__mutable_keys__, frozenset({'a', "__extra_items__"}))
4894-
self.assertEqual(Child.__annotations__, {"__extra_items__": int, "a": int})
4895-
self.assertEqual(Child.__extra_items__, ReadOnly[Union[str, None]])
4896-
self.assertFalse(Child.__closed__)
4949+
self.assertEqual(Child.__mutable_keys__, frozenset({'a'}))
4950+
self.assertEqual(Child.__annotations__, {"a": int})
4951+
self.assertIs(Child.__extra_items__, int)
4952+
self.assertIs(Child.__closed__, True)
48974953

48984954
class GrandChild(Child, closed=True):
48994955
__extra_items__: str
49004956

4901-
self.assertEqual(GrandChild.__required_keys__, frozenset({'a', "__extra_items__"}))
4957+
self.assertEqual(GrandChild.__required_keys__, frozenset({'a'}))
49024958
self.assertEqual(GrandChild.__optional_keys__, frozenset({}))
49034959
self.assertEqual(GrandChild.__readonly_keys__, frozenset({}))
4904-
self.assertEqual(GrandChild.__mutable_keys__, frozenset({'a', "__extra_items__"}))
4905-
self.assertEqual(GrandChild.__annotations__, {"__extra_items__": int, "a": int})
4906-
self.assertEqual(GrandChild.__extra_items__, str)
4907-
self.assertTrue(GrandChild.__closed__)
4960+
self.assertEqual(GrandChild.__mutable_keys__, frozenset({'a'}))
4961+
self.assertEqual(GrandChild.__annotations__, {"a": int})
4962+
self.assertIs(GrandChild.__extra_items__, str)
4963+
self.assertIs(GrandChild.__closed__, True)
4964+
4965+
def test_closed_inheritance(self):
4966+
class Base(TypedDict, extra_items=ReadOnly[Union[str, None]]):
4967+
a: int
4968+
4969+
self.assertEqual(Base.__required_keys__, frozenset({"a"}))
4970+
self.assertEqual(Base.__optional_keys__, frozenset({}))
4971+
self.assertEqual(Base.__readonly_keys__, frozenset({}))
4972+
self.assertEqual(Base.__mutable_keys__, frozenset({"a"}))
4973+
self.assertEqual(Base.__annotations__, {"a": int})
4974+
self.assertEqual(Base.__extra_items__, ReadOnly[Union[str, None]])
4975+
self.assertIsNone(Base.__closed__)
4976+
4977+
class Child(Base, extra_items=int):
4978+
a: str
4979+
4980+
self.assertEqual(Child.__required_keys__, frozenset({'a'}))
4981+
self.assertEqual(Child.__optional_keys__, frozenset({}))
4982+
self.assertEqual(Child.__readonly_keys__, frozenset({}))
4983+
self.assertEqual(Child.__mutable_keys__, frozenset({'a'}))
4984+
self.assertEqual(Child.__annotations__, {"a": str})
4985+
self.assertIs(Child.__extra_items__, int)
4986+
self.assertIsNone(Child.__closed__)
4987+
4988+
class GrandChild(Child, closed=True):
4989+
a: float
4990+
4991+
self.assertEqual(GrandChild.__required_keys__, frozenset({'a'}))
4992+
self.assertEqual(GrandChild.__optional_keys__, frozenset({}))
4993+
self.assertEqual(GrandChild.__readonly_keys__, frozenset({}))
4994+
self.assertEqual(GrandChild.__mutable_keys__, frozenset({'a'}))
4995+
self.assertEqual(GrandChild.__annotations__, {"a": float})
4996+
self.assertIs(GrandChild.__extra_items__, NoExtraItems)
4997+
self.assertIs(GrandChild.__closed__, True)
4998+
4999+
class GrandGrandChild(GrandChild):
5000+
...
5001+
self.assertEqual(GrandGrandChild.__required_keys__, frozenset({'a'}))
5002+
self.assertEqual(GrandGrandChild.__optional_keys__, frozenset({}))
5003+
self.assertEqual(GrandGrandChild.__readonly_keys__, frozenset({}))
5004+
self.assertEqual(GrandGrandChild.__mutable_keys__, frozenset({'a'}))
5005+
self.assertEqual(GrandGrandChild.__annotations__, {"a": float})
5006+
self.assertIs(GrandGrandChild.__extra_items__, NoExtraItems)
5007+
self.assertIsNone(GrandGrandChild.__closed__)
49085008

49095009
def test_implicit_extra_items(self):
49105010
class Base(TypedDict):
49115011
a: int
49125012

4913-
self.assertEqual(Base.__extra_items__, None)
4914-
self.assertFalse(Base.__closed__)
5013+
self.assertIs(Base.__extra_items__, NoExtraItems)
5014+
self.assertIsNone(Base.__closed__)
49155015

49165016
class ChildA(Base, closed=True):
49175017
...
49185018

4919-
self.assertEqual(ChildA.__extra_items__, Never)
4920-
self.assertTrue(ChildA.__closed__)
5019+
self.assertEqual(ChildA.__extra_items__, NoExtraItems)
5020+
self.assertIs(ChildA.__closed__, True)
49215021

5022+
@skipIf(TYPING_3_14_0, "Backwards compatibility only for Python 3.13")
5023+
def test_implicit_extra_items_before_3_14(self):
5024+
class Base(TypedDict):
5025+
a: int
49225026
class ChildB(Base, closed=True):
49235027
__extra_items__: None
49245028

4925-
self.assertEqual(ChildB.__extra_items__, type(None))
4926-
self.assertTrue(ChildB.__closed__)
5029+
self.assertIs(ChildB.__extra_items__, type(None))
5030+
self.assertIs(ChildB.__closed__, True)
49275031

49285032
@skipIf(
49295033
TYPING_3_13_0,
@@ -4933,9 +5037,14 @@ class ChildB(Base, closed=True):
49335037
def test_backwards_compatibility(self):
49345038
with self.assertWarns(DeprecationWarning):
49355039
TD = TypedDict("TD", closed=int)
4936-
self.assertFalse(TD.__closed__)
5040+
self.assertIs(TD.__closed__, None)
49375041
self.assertEqual(TD.__annotations__, {"closed": int})
49385042

5043+
with self.assertWarns(DeprecationWarning):
5044+
TD = TypedDict("TD", extra_items=int)
5045+
self.assertIs(TD.__extra_items__, NoExtraItems)
5046+
self.assertEqual(TD.__annotations__, {"extra_items": int})
5047+
49395048

49405049
class AnnotatedTests(BaseTestCase):
49415050

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.