@@ -4177,6 +4177,121 @@ class AllTheThings(TypedDict):
4177
4177
self .assertEqual (AllTheThings .__optional_keys__ , frozenset ({'c' , 'd' }))
4178
4178
self .assertEqual (AllTheThings .__readonly_keys__ , frozenset ({'a' , 'b' , 'c' }))
4179
4179
self .assertEqual (AllTheThings .__mutable_keys__ , frozenset ({'d' }))
4180
+
4181
+ def test_extra_keys_non_readonly (self ):
4182
+ class Base (TypedDict , closed = True ):
4183
+ __extra_items__ : str
4184
+
4185
+ class Child (Base ):
4186
+ a : NotRequired [int ]
4187
+
4188
+ self .assertEqual (Child .__required_keys__ , frozenset ({}))
4189
+ self .assertEqual (Child .__optional_keys__ , frozenset ({'a' }))
4190
+ self .assertEqual (Child .__readonly_keys__ , frozenset ({}))
4191
+ self .assertEqual (Child .__mutable_keys__ , frozenset ({'a' }))
4192
+
4193
+ def test_extra_keys_readonly (self ):
4194
+ class Base (TypedDict , closed = True ):
4195
+ __extra_items__ : ReadOnly [str ]
4196
+
4197
+ class Child (Base ):
4198
+ a : NotRequired [str ]
4199
+
4200
+ self .assertEqual (Child .__required_keys__ , frozenset ({}))
4201
+ self .assertEqual (Child .__optional_keys__ , frozenset ({'a' }))
4202
+ self .assertEqual (Child .__readonly_keys__ , frozenset ({}))
4203
+ self .assertEqual (Child .__mutable_keys__ , frozenset ({'a' }))
4204
+
4205
+ def test_extra_key_required (self ):
4206
+ with self .assertRaisesRegex (
4207
+ TypeError ,
4208
+ "Special key __extra_items__ does not support Required and NotRequired"
4209
+ ):
4210
+ TypedDict ("A" , {"__extra_items__" : Required [int ]}, closed = True )
4211
+
4212
+ with self .assertRaisesRegex (
4213
+ TypeError ,
4214
+ "Special key __extra_items__ does not support Required and NotRequired"
4215
+ ):
4216
+ TypedDict ("A" , {"__extra_items__" : NotRequired [int ]}, closed = True )
4217
+
4218
+ def test_regular_extra_items (self ):
4219
+ class ExtraReadOnly (TypedDict ):
4220
+ __extra_items__ : ReadOnly [str ]
4221
+
4222
+ class ExtraRequired (TypedDict ):
4223
+ __extra_items__ : Required [str ]
4224
+
4225
+ class ExtraNotRequired (TypedDict ):
4226
+ __extra_items__ : NotRequired [str ]
4227
+
4228
+ self .assertEqual (ExtraReadOnly .__required_keys__ , frozenset ({'__extra_items__' }))
4229
+ self .assertEqual (ExtraReadOnly .__optional_keys__ , frozenset ({}))
4230
+ self .assertEqual (ExtraReadOnly .__readonly_keys__ , frozenset ({'__extra_items__' }))
4231
+ self .assertEqual (ExtraReadOnly .__mutable_keys__ , frozenset ({}))
4232
+
4233
+ self .assertEqual (ExtraRequired .__required_keys__ , frozenset ({'__extra_items__' }))
4234
+ self .assertEqual (ExtraRequired .__optional_keys__ , frozenset ({}))
4235
+ self .assertEqual (ExtraRequired .__readonly_keys__ , frozenset ({}))
4236
+ self .assertEqual (ExtraRequired .__mutable_keys__ , frozenset ({'__extra_items__' }))
4237
+
4238
+ self .assertEqual (ExtraNotRequired .__required_keys__ , frozenset ({}))
4239
+ self .assertEqual (ExtraNotRequired .__optional_keys__ , frozenset ({'__extra_items__' }))
4240
+ self .assertEqual (ExtraNotRequired .__readonly_keys__ , frozenset ({}))
4241
+ self .assertEqual (ExtraNotRequired .__mutable_keys__ , frozenset ({'__extra_items__' }))
4242
+
4243
+ def test_closed_inheritance (self ):
4244
+ class Base (TypedDict , closed = True ):
4245
+ __extra_items__ : ReadOnly [Union [str , None ]]
4246
+
4247
+ class Child (Base ):
4248
+ a : int
4249
+ __extra_items__ : int
4250
+
4251
+ class GrandChild (Child , closed = True ):
4252
+ __extra_items__ : str
4253
+
4254
+ self .assertEqual (Base .__required_keys__ , frozenset ({}))
4255
+ self .assertEqual (Base .__optional_keys__ , frozenset ({}))
4256
+ self .assertEqual (Base .__readonly_keys__ , frozenset ({}))
4257
+ self .assertEqual (Base .__mutable_keys__ , frozenset ({}))
4258
+ self .assertEqual (Base .__extra_items__ , ReadOnly [Union [str , None ]])
4259
+
4260
+ self .assertEqual (Child .__required_keys__ , frozenset ({'a' , "__extra_items__" }))
4261
+ self .assertEqual (Child .__optional_keys__ , frozenset ({}))
4262
+ self .assertEqual (Child .__readonly_keys__ , frozenset ({}))
4263
+ self .assertEqual (Child .__mutable_keys__ , frozenset ({'a' , "__extra_items__" }))
4264
+ self .assertEqual (Child .__extra_items__ , ReadOnly [Union [str , None ]])
4265
+
4266
+ self .assertEqual (GrandChild .__required_keys__ , frozenset ({'a' , "__extra_items__" }))
4267
+ self .assertEqual (GrandChild .__optional_keys__ , frozenset ({}))
4268
+ self .assertEqual (GrandChild .__readonly_keys__ , frozenset ({}))
4269
+ self .assertEqual (GrandChild .__mutable_keys__ , frozenset ({'a' , "__extra_items__" }))
4270
+ self .assertEqual (GrandChild .__extra_items__ , str )
4271
+
4272
+ self .assertEqual (Base .__annotations__ , {})
4273
+ self .assertEqual (Child .__annotations__ , {"__extra_items__" : int , "a" : int })
4274
+ self .assertEqual (GrandChild .__annotations__ , {"__extra_items__" : int , "a" : int })
4275
+
4276
+ self .assertTrue (Base .__closed__ )
4277
+ self .assertFalse (Child .__closed__ )
4278
+ self .assertTrue (GrandChild .__closed__ )
4279
+
4280
+ def test_absent_extra_items (self ):
4281
+ class Base (TypedDict ):
4282
+ a : int
4283
+
4284
+ class ChildA (Base , closed = True ):
4285
+ ...
4286
+
4287
+ class ChildB (Base , closed = True ):
4288
+ __extra_items__ : None
4289
+
4290
+ self .assertNotIn ("__extra_items__" , Base .__dict__ )
4291
+ self .assertIn ("__extra_items__" , ChildA .__dict__ )
4292
+ self .assertIn ("__extra_items__" , ChildB .__dict__ )
4293
+ self .assertEqual (ChildA .__extra_items__ , Never )
4294
+ self .assertEqual (ChildB .__extra_items__ , type (None ))
4180
4295
4181
4296
4182
4297
class AnnotatedTests (BaseTestCase ):
0 commit comments