5555 Never ,
5656 NewType ,
5757 NoDefault ,
58+ NoExtraItems ,
5859 NoReturn ,
5960 NotRequired ,
6061 Optional ,
128129# 3.13.0.rc1 fixes a problem with @deprecated
129130TYPING_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
133136HAS_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
49405049class AnnotatedTests (BaseTestCase ):
49415050
0 commit comments