23
23
from typing import reveal_type
24
24
from typing import no_type_check , no_type_check_decorator
25
25
from typing import Type
26
- from typing import NamedTuple , TypedDict
26
+ from typing import NamedTuple , NotRequired , Required , TypedDict
27
27
from typing import IO , TextIO , BinaryIO
28
28
from typing import Pattern , Match
29
29
from typing import Annotated , ForwardRef
@@ -3993,6 +3993,26 @@ class Options(TypedDict, total=False):
3993
3993
log_level : int
3994
3994
log_path : str
3995
3995
3996
+ class TotalMovie (TypedDict ):
3997
+ title : str
3998
+ year : NotRequired [int ]
3999
+
4000
+ class NontotalMovie (TypedDict , total = False ):
4001
+ title : Required [str ]
4002
+ year : int
4003
+
4004
+ class AnnotatedMovie (TypedDict ):
4005
+ title : Annotated [Required [str ], "foobar" ]
4006
+ year : NotRequired [Annotated [int , 2000 ]]
4007
+
4008
+ class DeeplyAnnotatedMovie (TypedDict ):
4009
+ title : Annotated [Annotated [Required [str ], "foobar" ], "another level" ]
4010
+ year : NotRequired [Annotated [int , 2000 ]]
4011
+
4012
+ class WeirdlyQuotedMovie (TypedDict ):
4013
+ title : Annotated ['Annotated[Required[str], "foobar"]' , "another level" ]
4014
+ year : NotRequired ['Annotated[int, 2000]' ]
4015
+
3996
4016
class HasForeignBaseClass (mod_generics_cache .A ):
3997
4017
some_xrepr : 'XRepr'
3998
4018
other_a : 'mod_generics_cache.A'
@@ -4280,6 +4300,36 @@ def test_top_level_class_var(self):
4280
4300
):
4281
4301
get_type_hints (ann_module6 )
4282
4302
4303
+ def test_get_type_hints_typeddict (self ):
4304
+ self .assertEqual (get_type_hints (TotalMovie ), {'title' : str , 'year' : int })
4305
+ self .assertEqual (get_type_hints (TotalMovie , include_extras = True ), {
4306
+ 'title' : str ,
4307
+ 'year' : NotRequired [int ],
4308
+ })
4309
+
4310
+ self .assertEqual (get_type_hints (AnnotatedMovie ), {'title' : str , 'year' : int })
4311
+ self .assertEqual (get_type_hints (AnnotatedMovie , include_extras = True ), {
4312
+ 'title' : Annotated [Required [str ], "foobar" ],
4313
+ 'year' : NotRequired [Annotated [int , 2000 ]],
4314
+ })
4315
+
4316
+ self .assertEqual (get_type_hints (DeeplyAnnotatedMovie ), {'title' : str , 'year' : int })
4317
+ self .assertEqual (get_type_hints (DeeplyAnnotatedMovie , include_extras = True ), {
4318
+ 'title' : Annotated [Required [str ], "foobar" , "another level" ],
4319
+ 'year' : NotRequired [Annotated [int , 2000 ]],
4320
+ })
4321
+
4322
+ self .assertEqual (get_type_hints (WeirdlyQuotedMovie ), {'title' : str , 'year' : int })
4323
+ self .assertEqual (get_type_hints (WeirdlyQuotedMovie , include_extras = True ), {
4324
+ 'title' : Annotated [Required [str ], "foobar" , "another level" ],
4325
+ 'year' : NotRequired [Annotated [int , 2000 ]],
4326
+ })
4327
+
4328
+ self .assertEqual (get_type_hints (_typed_dict_helper .VeryAnnotated ), {'a' : int })
4329
+ self .assertEqual (get_type_hints (_typed_dict_helper .VeryAnnotated , include_extras = True ), {
4330
+ 'a' : Annotated [Required [int ], "a" , "b" , "c" ]
4331
+ })
4332
+
4283
4333
4284
4334
class GetUtilitiesTestCase (TestCase ):
4285
4335
def test_get_origin (self ):
@@ -4305,6 +4355,8 @@ class C(Generic[T]): pass
4305
4355
self .assertIs (get_origin (list | str ), types .UnionType )
4306
4356
self .assertIs (get_origin (P .args ), P )
4307
4357
self .assertIs (get_origin (P .kwargs ), P )
4358
+ self .assertIs (get_origin (Required [int ]), Required )
4359
+ self .assertIs (get_origin (NotRequired [int ]), NotRequired )
4308
4360
4309
4361
def test_get_args (self ):
4310
4362
T = TypeVar ('T' )
@@ -4342,6 +4394,8 @@ class C(Generic[T]): pass
4342
4394
self .assertEqual (get_args (Callable [Concatenate [int , P ], int ]),
4343
4395
(Concatenate [int , P ], int ))
4344
4396
self .assertEqual (get_args (list | str ), (list , str ))
4397
+ self .assertEqual (get_args (Required [int ]), (int ,))
4398
+ self .assertEqual (get_args (NotRequired [int ]), (int ,))
4345
4399
4346
4400
4347
4401
class CollectionsAbcTests (BaseTestCase ):
@@ -5299,6 +5353,32 @@ class Cat(Animal):
5299
5353
'voice' : str ,
5300
5354
}
5301
5355
5356
+ def test_required_notrequired_keys (self ):
5357
+ self .assertEqual (NontotalMovie .__required_keys__ ,
5358
+ frozenset ({"title" }))
5359
+ self .assertEqual (NontotalMovie .__optional_keys__ ,
5360
+ frozenset ({"year" }))
5361
+
5362
+ self .assertEqual (TotalMovie .__required_keys__ ,
5363
+ frozenset ({"title" }))
5364
+ self .assertEqual (TotalMovie .__optional_keys__ ,
5365
+ frozenset ({"year" }))
5366
+
5367
+ self .assertEqual (_typed_dict_helper .VeryAnnotated .__required_keys__ ,
5368
+ frozenset ())
5369
+ self .assertEqual (_typed_dict_helper .VeryAnnotated .__optional_keys__ ,
5370
+ frozenset ({"a" }))
5371
+
5372
+ self .assertEqual (AnnotatedMovie .__required_keys__ ,
5373
+ frozenset ({"title" }))
5374
+ self .assertEqual (AnnotatedMovie .__optional_keys__ ,
5375
+ frozenset ({"year" }))
5376
+
5377
+ self .assertEqual (WeirdlyQuotedMovie .__required_keys__ ,
5378
+ frozenset ({"title" }))
5379
+ self .assertEqual (WeirdlyQuotedMovie .__optional_keys__ ,
5380
+ frozenset ({"year" }))
5381
+
5302
5382
def test_multiple_inheritance (self ):
5303
5383
class One (TypedDict ):
5304
5384
one : int
@@ -5399,6 +5479,98 @@ def test_get_type_hints(self):
5399
5479
)
5400
5480
5401
5481
5482
+ class RequiredTests (BaseTestCase ):
5483
+
5484
+ def test_basics (self ):
5485
+ with self .assertRaises (TypeError ):
5486
+ Required [NotRequired ]
5487
+ with self .assertRaises (TypeError ):
5488
+ Required [int , str ]
5489
+ with self .assertRaises (TypeError ):
5490
+ Required [int ][str ]
5491
+
5492
+ def test_repr (self ):
5493
+ self .assertEqual (repr (Required ), 'typing.Required' )
5494
+ cv = Required [int ]
5495
+ self .assertEqual (repr (cv ), 'typing.Required[int]' )
5496
+ cv = Required [Employee ]
5497
+ self .assertEqual (repr (cv ), f'typing.Required[{ __name__ } .Employee]' )
5498
+
5499
+ def test_cannot_subclass (self ):
5500
+ with self .assertRaises (TypeError ):
5501
+ class C (type (Required )):
5502
+ pass
5503
+ with self .assertRaises (TypeError ):
5504
+ class C (type (Required [int ])):
5505
+ pass
5506
+ with self .assertRaises (TypeError ):
5507
+ class C (Required ):
5508
+ pass
5509
+ with self .assertRaises (TypeError ):
5510
+ class C (Required [int ]):
5511
+ pass
5512
+
5513
+ def test_cannot_init (self ):
5514
+ with self .assertRaises (TypeError ):
5515
+ Required ()
5516
+ with self .assertRaises (TypeError ):
5517
+ type (Required )()
5518
+ with self .assertRaises (TypeError ):
5519
+ type (Required [Optional [int ]])()
5520
+
5521
+ def test_no_isinstance (self ):
5522
+ with self .assertRaises (TypeError ):
5523
+ isinstance (1 , Required [int ])
5524
+ with self .assertRaises (TypeError ):
5525
+ issubclass (int , Required )
5526
+
5527
+
5528
+ class NotRequiredTests (BaseTestCase ):
5529
+
5530
+ def test_basics (self ):
5531
+ with self .assertRaises (TypeError ):
5532
+ NotRequired [Required ]
5533
+ with self .assertRaises (TypeError ):
5534
+ NotRequired [int , str ]
5535
+ with self .assertRaises (TypeError ):
5536
+ NotRequired [int ][str ]
5537
+
5538
+ def test_repr (self ):
5539
+ self .assertEqual (repr (NotRequired ), 'typing.NotRequired' )
5540
+ cv = NotRequired [int ]
5541
+ self .assertEqual (repr (cv ), 'typing.NotRequired[int]' )
5542
+ cv = NotRequired [Employee ]
5543
+ self .assertEqual (repr (cv ), f'typing.NotRequired[{ __name__ } .Employee]' )
5544
+
5545
+ def test_cannot_subclass (self ):
5546
+ with self .assertRaises (TypeError ):
5547
+ class C (type (NotRequired )):
5548
+ pass
5549
+ with self .assertRaises (TypeError ):
5550
+ class C (type (NotRequired [int ])):
5551
+ pass
5552
+ with self .assertRaises (TypeError ):
5553
+ class C (NotRequired ):
5554
+ pass
5555
+ with self .assertRaises (TypeError ):
5556
+ class C (NotRequired [int ]):
5557
+ pass
5558
+
5559
+ def test_cannot_init (self ):
5560
+ with self .assertRaises (TypeError ):
5561
+ NotRequired ()
5562
+ with self .assertRaises (TypeError ):
5563
+ type (NotRequired )()
5564
+ with self .assertRaises (TypeError ):
5565
+ type (NotRequired [Optional [int ]])()
5566
+
5567
+ def test_no_isinstance (self ):
5568
+ with self .assertRaises (TypeError ):
5569
+ isinstance (1 , NotRequired [int ])
5570
+ with self .assertRaises (TypeError ):
5571
+ issubclass (int , NotRequired )
5572
+
5573
+
5402
5574
class IOTests (BaseTestCase ):
5403
5575
5404
5576
def test_io (self ):
0 commit comments