@@ -439,6 +439,48 @@ def assertNotIsSubclass(self, cls, class_or_tuple, msg=None):
439439 raise self .failureException (message )
440440
441441
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 .__owner__ != 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+
442484class Employee :
443485 pass
444486
@@ -5152,6 +5194,64 @@ def test_inline(self):
51525194 self .assertIs (type (inst ), dict )
51535195 self .assertEqual (inst ["a" ], 1 )
51545196
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+
51555255class AnnotatedTests (BaseTestCase ):
51565256
51575257 def test_repr (self ):
@@ -5963,7 +6063,7 @@ def test_substitution(self):
59636063 U2 = Unpack [Ts ]
59646064 self .assertEqual (C2 [U1 ], (str , int , str ))
59656065 self .assertEqual (C2 [U2 ], (str , Unpack [Ts ]))
5966- self .assertEqual (C2 ["U2" ], (str , typing . ForwardRef ("U2" )))
6066+ self .assertEqual (C2 ["U2" ], (str , EqualToForwardRef ("U2" )))
59676067
59686068 if (3 , 12 , 0 ) <= sys .version_info < (3 , 12 , 4 ):
59696069 with self .assertRaises (AssertionError ):
@@ -7250,8 +7350,8 @@ def test_or(self):
72507350 self .assertEqual (X | "x" , Union [X , "x" ])
72517351 self .assertEqual ("x" | X , Union ["x" , X ])
72527352 # 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 ))
72557355
72567356 def test_union_constrained (self ):
72577357 A = TypeVar ('A' , str , bytes )
@@ -8819,7 +8919,7 @@ class X:
88198919 type_params = None ,
88208920 format = Format .FORWARDREF ,
88218921 )
8822- self .assertEqual (evaluated_ref , typing . ForwardRef ("doesnotexist2" ))
8922+ self .assertEqual (evaluated_ref , EqualToForwardRef ("doesnotexist2" ))
88238923
88248924 def test_evaluate_with_type_params (self ):
88258925 # Use a T name that is not in globals
@@ -8906,13 +9006,6 @@ def test_fwdref_with_globals(self):
89069006 obj = object ()
89079007 self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" ), globals = {"int" : obj }), obj )
89089008
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-
89169009 def test_fwdref_with_owner (self ):
89179010 self .assertEqual (
89189011 evaluate_forward_ref (typing .ForwardRef ("Counter[int]" ), owner = collections ),
@@ -8956,7 +9049,7 @@ class Y(Generic[Tx]):
89569049 self .assertEqual (get_args (evaluated_ref1b ), (Y [Tx ],))
89579050
89589051 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 })
89609053 self .assertEqual (get_origin (evaluated_ref2 ), Y )
89619054 self .assertEqual (get_args (evaluated_ref2 ), (Y [Tx ],))
89629055
0 commit comments