@@ -439,6 +439,48 @@ def assertNotIsSubclass(self, cls, class_or_tuple, msg=None):
439
439
raise self .failureException (message )
440
440
441
441
442
+ class EqualToForwardRef :
443
+ """Helper to ease use of annotationlib.ForwardRef in tests.
444
+
445
+ This checks only attributes that can be set using the constructor.
446
+
447
+ """
448
+
449
+ def __init__ (
450
+ self ,
451
+ arg ,
452
+ * ,
453
+ module = None ,
454
+ owner = None ,
455
+ is_class = False ,
456
+ ):
457
+ self .__forward_arg__ = arg
458
+ self .__forward_is_class__ = is_class
459
+ self .__forward_module__ = module
460
+ self .__owner__ = owner
461
+
462
+ def __eq__ (self , other ):
463
+ if not isinstance (other , (EqualToForwardRef , typing .ForwardRef )):
464
+ return NotImplemented
465
+ if sys .version_info >= (3 , 14 ) and self .__owne
9E88
r__ != other .__owner__ :
466
+ return False
467
+ return (
468
+ self .__forward_arg__ == other .__forward_arg__
469
+ and self .__forward_module__ == other .__forward_module__
470
+ and self .__forward_is_class__ == other .__forward_is_class__
471
+ )
472
+
473
+ def __repr__ (self ):
474
+ extra = []
475
+ if self .__forward_module__ is not None :
476
+ extra .append (f", module={ self .__forward_module__ !r} " )
477
+ if self .__forward_is_class__ :
478
+ extra .append (", is_class=True" )
479
+ if sys .version_info >= (3 , 14 ) and self .__owner__ is not None :
480
+ extra .append (f", owner={ self .__owner__ !r} " )
481
+ return f"EqualToForwardRef({ self .__forward_arg__ !r} { '' .join (extra )} )"
482
+
483
+
442
484
class Employee :
443
485
pass
444
486
@@ -5152,6 +5194,64 @@ def test_inline(self):
5152
5194
self .assertIs (type (inst ), dict )
5153
5195
self .assertEqual (inst ["a" ], 1 )
5154
5196
5197
+ def test_annotations (self ):
5198
+ # _type_check is applied
5199
+ with self .assertRaisesRegex (TypeError , "Plain typing.Optional is not valid as type argument" ):
5200
+ class X (TypedDict ):
5201
+ a : Optional
5202
+
5203
+ # _type_convert is applied
5204
+ class Y (TypedDict ):
5205
+ a : None
5206
+ b : "int"
5207
+ if sys .version_info >= (3 , 14 ):
5208
+ import annotationlib
5209
+
5210
+ fwdref = EqualToForwardRef ('int' , module = __name__ )
5211
+ self .assertEqual (Y .__annotations__ , {'a' : type (None ), 'b' : fwdref })
5212
+ self .assertEqual (Y .__annotate__ (annotationlib .Format .FORWARDREF ), {'a' : type (None ), 'b' : fwdref })
5213
+ else :
5214
+ self .assertEqual (Y .__annotations__ , {'a' : type (None ), 'b' : typing .ForwardRef ('int' , module = __name__ )})
5215
+
5216
+ @skipUnless (TYPING_3_14_0 , "Only supported on 3.14" )
5217
+ def test_delayed_type_check (self ):
5218
+ # _type_check is also applied later
5219
+ class Z (TypedDict ):
5220
+ a : undefined # noqa: F821
5221
+
5222
+ with self .assertRaises (NameError ):
5223
+ Z .__annotations__
5224
+
5225
+ undefined = Final
5226
+ with self .assertRaisesRegex (TypeError , "Plain typing.Final is not valid as type argument" ):
5227
+ Z .__annotations__
5228
+
5229
+ undefined = None # noqa: F841
5230
+ self .assertEqual (Z .__annotations__ , {'a' : type (None )})
5231
+
5232
+ @skipUnless (TYPING_3_14_0 , "Only supported on 3.14" )
5233
+ def test_deferred_evaluation (self ):
5234
+ class A (TypedDict ):
5235
+ x : NotRequired [undefined ] # noqa: F821
5236
+ y : ReadOnly [undefined ] # noqa: F821
5237
+ z : Required [undefined ] # noqa: F821
5238
+
5239
+ self .assertEqual (A .__required_keys__ , frozenset ({'y' , 'z' }))
5240
+ self .assertEqual (A .__optional_keys__ , frozenset ({'x' }))
5241
+ self .assertEqual (A .__readonly_keys__ , frozenset ({'y' }))
5242
+ self .assertEqual (A .__mutable_keys__ , frozenset ({'x' , 'z' }))
5243
+
5244
+ with self .assertRaises (NameError ):
5245
+ A .__annotations__
5246
+
5247
+ import annotationlib
5248
+ self .assertEqual (
5249
+ A .__annotate__ (annotationlib .Format .STRING ),
5250
+ {'x' : 'NotRequired[undefined]' , 'y' : 'ReadOnly[undefined]' ,
5251
+ 'z' : 'Required[undefined]' },
5252
+ )
5253
+
5254
+
5155
5255
class AnnotatedTests (BaseTestCase ):
5156
5256
5157
5257
def test_repr (self ):
@@ -5963,7 +6063,7 @@ def test_substitution(self):
5963
6063
U2 = Unpack [Ts ]
5964
6064
self .assertEqual (C2 [U1 ], (str , int , str ))
5965
6065
self .assertEqual (C2 [U2 ], (str , Unpack [Ts ]))
5966
- self .assertEqual (C2 ["U2" ], (str , typing . ForwardRef ("U2" )))
6066
+ self .assertEqual (C2 ["U2" ], (str , EqualToForwardRef ("U2" )))
5967
6067
5968
6068
if (3 , 12 , 0 ) <= sys .version_info < (3 , 12 , 4 ):
5969
6069
with self .assertRaises (AssertionError ):
@@ -7250,8 +7350,8 @@ def test_or(self):
7250
7350
self .assertEqual (X | "x" , Union [X , "x" ])
7251
7351
self .assertEqual ("x" | X , Union ["x" , X ])
7252
7352
# make sure the order is correct
7253
- self .assertEqual (get_args (X | "x" ), (X , typing . ForwardRef ("x" )))
7254
- self .assertEqual (get_args ("x" | X ), (typing . ForwardRef ("x" ), X ))
7353
+ self .assertEqual (get_args (X | "x" ), (X , EqualToForwardRef ("x" )))
7354
+ self .assertEqual (get_args ("x" | X ), (EqualToForwardRef ("x" ), X ))
7255
7355
7256
7356
def test_union_constrained (self ):
7257
7357
A = TypeVar ('A' , str , bytes )
@@ -8819,7 +8919,7 @@ class X:
8819
8919
type_params = None ,
8820
8920
format = Format .FORWARDREF ,
8821
8921
)
8822
- self .assertEqual (evaluated_ref , typing . ForwardRef ("doesnotexist2" ))
8922
+ self .assertEqual (evaluated_ref , EqualToForwardRef ("doesnotexist2" ))
8823
8923
8824
8924
def test_evaluate_with_type_params (self ):
8825
8925
# Use a T name that is not in globals
@@ -8906,13 +9006,6 @@ def test_fwdref_with_globals(self):
8906
9006
obj = object ()
8907
9007
self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" ), globals = {"int" : obj }), obj )
8908
9008
8909
- def test_fwdref_value_is_cached (self ):
8910
- fr = typing .ForwardRef ("hello" )
8911
- with self .assertRaises (NameError ):
8912
- evaluate_forward_ref (fr )
8913
- self .assertIs (evaluate_forward_ref (fr , globals = {"hello" : str }), str )
8914
- self .assertIs (evaluate_forward_ref (fr ), str )
8915
-
8916
9009
def test_fwdref_with_owner (self ):
8917
9010
self .assertEqual (
8918
9011
evaluate_forward_ref (typing .ForwardRef ("Counter[int]" ), owner = collections ),
@@ -8956,7 +9049,7 @@ class Y(Generic[Tx]):
8956
9049
self .assertEqual (get_args (evaluated_ref1b ), (Y [Tx ],))
8957
9050
8958
9051
with self .subTest ("nested string of TypeVar" ):
8959
- evaluated_ref2 = evaluate_forward_ref (typing .ForwardRef ("""Y["Y['Tx']"]""" ), locals = {"Y" : Y })
9052
+ evaluated_ref2 = evaluate_forward_ref (typing .ForwardRef ("""Y["Y['Tx']"]""" ), locals = {"Y" : Y , "Tx" : Tx })
8960
9053
self .assertEqual (get_origin (evaluated_ref2 ), Y )
8961
9054
self .assertEqual (get_args (evaluated_ref2 ), (Y [Tx ],))
8962
9055
0 commit comments