2828import typing_extensions
2929from _typed_dict_test_helper import Foo , FooGeneric , VeryAnnotated
3030from typing_extensions import (
31+ _FORWARD_REF_HAS_CLASS ,
3132 _PEP_649_OR_749_IMPLEMENTED ,
3233 Annotated ,
3334 Any ,
8283 clear_overloads ,
8384 dataclass_transform ,
8485 deprecated ,
86+ evaluate_forward_ref ,
8587 final ,
8688 get_annotations ,
8789 get_args ,
@@ -7948,7 +7950,7 @@ def f2(a: "undefined"): # noqa: F821
79487950 self .assertEqual (get_annotations (f2 , format = 2 ), {"a" : "undefined" })
79497951
79507952 self .assertEqual (
7951- get_annotations (f1 , format = Format .SOURCE ),
7953+ get_annotations (f1 , format = Format .STRING ),
79527954 {"a" : "int" },
79537955 )
79547956 self .assertEqual (get_annotations (f1 , format = 3 ), {"a" : "int" })
@@ -7975,7 +7977,7 @@ def foo():
79757977 foo , format = Format .FORWARDREF , eval_str = True
79767978 )
79777979 get_annotations (
7978- foo , format = Format .SOURCE , eval_str = True
7980+ foo , format = Format .STRING , eval_str = True
79797981 )
79807982
79817983 def test_stock_annotations (self ):
@@ -7989,7 +7991,7 @@ def foo(a: int, b: str):
79897991 {"a" : int , "b" : str },
79907992 )
79917993 self .assertEqual (
7992- get_annotations (foo , format = Format .SOURCE ),
7994+ get_annotations (foo , format = Format .STRING ),
79937995 {"a" : "int" , "b" : "str" },
79947996 )
79957997
@@ -8084,43 +8086,43 @@ def test_stock_annotations_in_module(self):
80848086 )
80858087
80868088 self .assertEqual (
8087- get_annotations (isa , format = Format .SOURCE ),
8089+ get_annotations (isa , format = Format .STRING ),
80888090 {"a" : "int" , "b" : "str" },
80898091 )
80908092 self .assertEqual (
8091- get_annotations (isa .MyClass , format = Format .SOURCE ),
8093+ get_annotations (isa .MyClass , format = Format .STRING ),
80928094 {"a" : "int" , "b" : "str" },
80938095 )
80948096 mycls = "MyClass" if _PEP_649_OR_749_IMPLEMENTED else "inspect_stock_annotations.MyClass"
80958097 self .assertEqual (
8096- get_annotations (isa .function , format = Format .SOURCE ),
8098+ get_annotations (isa .function , format = Format .STRING ),
80978099 {"a" : "int" , "b" : "str" , "return" : mycls },
80988100 )
80998101 self .assertEqual (
81008102 get_annotations (
8101- isa .function2 , format = Format .SOURCE
8103+ isa .function2 , format = Format .STRING
81028104 ),
81038105 {"a" : "int" , "b" : "str" , "c" : mycls , "return" : mycls },
81048106 )
81058107 self .assertEqual (
81068108 get_annotations (
8107- isa .function3 , format = Format .SOURCE
8109+ isa .function3 , format = Format .STRING
81088110 ),
81098111 {"a" : "int" , "b" : "str" , "c" : "MyClass" },
81108112 )
81118113 self .assertEqual (
8112- get_annotations (inspect , format = Format .SOURCE ),
8114+ get_annotations (inspect , format = Format .STRING ),
81138115 {},
81148116 )
81158117 self .assertEqual (
81168118 get_annotations (
8117- isa .UnannotatedClass , format = Format .SOURCE
8119+ isa .UnannotatedClass , format = Format .STRING
81188120 ),
81198121 {},
81208122 )
81218123 self .assertEqual (
81228124 get_annotations (
8123- isa .unannotated_function , format = Format .SOURCE
8125+ isa .unannotated_function , format = Format .STRING
81248126 ),
81258127 {},
81268128 )
@@ -8141,7 +8143,7 @@ def test_stock_annotations_on_wrapper(self):
81418143 )
81428144 mycls = "MyClass" if _PEP_649_OR_749_IMPLEMENTED else "inspect_stock_annotations.MyClass"
81438145 self .assertEqual (
8144- get_annotations (wrapped , format = Format .SOURCE ),
8146+ get_annotations (wrapped , format = Format .STRING ),
81458147 {"a" : "int" , "b" : "str" , "return" : mycls },
81468148 )
81478149 self .assertEqual (
@@ -8160,10 +8162,10 @@ def test_stringized_annotations_in_module(self):
81608162 {"eval_str" : False },
81618163 {"format" : Format .VALUE },
81628164 {"format" : Format .FORWARDREF },
8163- {"format" : Format .SOURCE },
8165+ {"format" : Format .STRING },
81648166 {"format" : Format .VALUE , "eval_str" : False },
81658167 {"format" : Format .FORWARDREF , "eval_str" : False },
8166- {"format" : Format .SOURCE , "eval_str" : False },
8168+ {"format" : Format .STRING , "eval_str" : False },
81678169 ]:
81688170 with self .subTest (** kwargs ):
81698171 self .assertEqual (
@@ -8466,6 +8468,204 @@ def test_pep_695_generics_with_future_annotations_nested_in_function(self):
84668468 set (results .generic_func .__type_params__ )
84678469 )
84688470
8471+ class TestEvaluateForwardRefs (BaseTestCase ):
8472+ def test_global_constant (self ):
8473+ if sys .version_info [:3 ] > (3 , 10 , 0 ):
8474+ self .assertTrue (_FORWARD_REF_HAS_CLASS )
8475+
8476+ def test_forward_ref_fallback (self ):
8477+ with self .assertRaises (NameError ):
8478+ evaluate_forward_ref (typing .ForwardRef ("doesntexist" ))
8479+ ref = typing .ForwardRef ("doesntexist" )
8480+ self .assertIs (evaluate_forward_ref (ref , format = Format .FORWARDREF ), ref )
8481+
8482+ class X :
8483+ unresolvable = "doesnotexist2"
8484+
8485+ evaluated_ref = evaluate_forward_ref (
8486+ typing .ForwardRef ("X.unresolvable" ),
8487+ locals = {"X" : X },
8488+ type_params = None ,
8489+ format = Format .FORWARDREF ,
8490+ )
8491+ self .assertEqual (evaluated_ref , typing .ForwardRef ("doesnotexist2" ))
8492+
8493+ def test_evaluate_with_type_params (self ):
8494+ # Use a T name that is not in globals
8495+ self .assertNotIn ("Tx" , globals ())
8496+ if not TYPING_3_12_0 :
8497+ Tx = TypeVar ("Tx" )
8498+ class Gen (Generic [Tx ]):
8499+ alias = int
8500+ if not hasattr (Gen , "__type_params__" ):
8501+ Gen .__type_params__ = (Tx ,)
8502+ self .assertEqual (Gen .__type_params__ , (Tx ,))
8503+ del Tx
8504+ else :
8505+ ns = {}
8506+ exec (textwrap .dedent ("""
8507+ class Gen[Tx]:
8508+ alias = int
8509+ """ ), None , ns )
8510+ Gen = ns ["Gen" ]
8511+
8512+ # owner=None, type_params=None
8513+ # NOTE: The behavior of owner=None might change in the future when ForwardRef.__owner__ is available
8514+ with self .assertRaises (NameError ):
8515+ evaluate_forward_ref (typing .ForwardRef ("Tx" ))
8516+ with self .assertRaises (NameError ):
8517+ evaluate_forward_ref (typing .ForwardRef ("Tx" ), type_params = ())
8518+ with self .assertRaises (NameError ):
8519+ evaluate_forward_ref (typing .ForwardRef ("Tx" ), owner = int )
8520+
8521+ (Tx ,) = Gen .__type_params__
8522+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("Tx" ), type_params = Gen .__type_params__ ), Tx )
8523+
8524+ # For this test its important that Tx is not a global variable, i.e. do not use "T" here
8525+ self .assertNotIn ("Tx" , globals ())
8526+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("Tx" ), owner = Gen ), Tx )
8527+
8528+ # Different type_params take precedence
8529+ not_Tx = TypeVar ("Tx" ) # different TypeVar with same name
8530+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("Tx" ), type_params = (not_Tx ,), owner = Gen ), not_Tx )
8531+
8532+ # globals can take higher precedence
8533+ if _FORWARD_REF_HAS_CLASS :
8534+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("Tx" , is_class = True ), owner = Gen , globals = {"Tx" : str }), str )
8535+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("Tx" , is_class = True ), owner = Gen , type_params = (not_Tx ,), globals = {"Tx" : str }), str )
8536+
8537+ with self .assertRaises (NameError ):
8538+ evaluate_forward_ref (typing .ForwardRef ("alias" ), type_params = Gen .__type_params__ )
8539+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("alias" ), owner = Gen ), int )
8540+ # If you pass custom locals, we don't look at the owner's locals
8541+ with self .assertRaises (NameError ):
8542+ evaluate_forward_ref (typing .ForwardRef ("alias" ), owner = Gen , locals = {})
8543+ # But if the name exists in the locals, it works
8544+ self .assertIs (
8545+ evaluate_forward_ref (typing .ForwardRef ("alias" ), owner = Gen , locals = {"alias" : str }), str
8546+ )
8547+
8548+ @skipUnless (
8549+ HAS_FORWARD_MODULE , "Needs module 'forward' to test forward references"
8550+ )
8551+ def test_fwdref_with_module (self ):
8552+ self .assertIs (
8553+ evaluate_forward_ref (typing .ForwardRef ("Counter" , module = "collections" )), collections .Counter
8554+ )
8555+ self .assertEqual (
8556+ evaluate_forward_ref (typing .ForwardRef ("Counter[int]" , module = "collections" )),
8557+ collections .Counter [int ],
8558+ )
8559+
8560+ with self .assertRaises (NameError ):
8561+ # If globals are passed explicitly, we don't look at the module dict
8562+ evaluate_forward_ref (typing .ForwardRef ("Format" , module = "annotationlib" ), globals = {})
8563+
8564+ def test_fwdref_to_builtin (self ):
8565+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" )), int )
8566+ if HAS_FORWARD_MODULE :
8567+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" , module = "collections" )), int )
8568+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" ), owner = str ), int )
8569+
8570+ # builtins are still searched with explicit globals
8571+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" ), globals = {}), int )
8572+
8573+ def test_fwdref_with_globals (self ):
8574+ # explicit values in globals have precedence
8575+ obj = object ()
8576+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" ), globals = {"int" : obj }), obj )
8577+
8578+ def test_fwdref_value_is_cached (self ):
8579+ fr = typing .ForwardRef ("hello" )
8580+ with self .assertRaises (NameError ):
8581+ evaluate_forward_ref (fr )
8582+ self .assertIs (evaluate_forward_ref (fr , globals = {"hello" : str }), str )
8583+ self .assertIs (evaluate_forward_ref (fr ), str )
8584+
8585+ @skipUnless (TYPING_3_9_0 , "Needs PEP 585 support" )
8586+ def test_fwdref_with_owner (self ):
8587+ self .assertEqual (
8588+ evaluate_forward_ref (typing .ForwardRef ("Counter[int]" ), owner = collections ),
8589+ collections .Counter [int ],
8590+ )
8591+
8592+ def test_name_lookup_without_eval (self ):
8593+ # test the codepath where we look up simple names directly in the
8594+ # namespaces without going through eval()
8595+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" )), int )
8596+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" ), locals = {"int" : str }), str )
8597+ self .assertIs (
8598+ evaluate_forward_ref (typing .ForwardRef ("int" ), locals = {"int" : float }, globals = {"int" : str }),
8599+ float ,
8600+ )
8601+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" ), globals = {"int" : str }), str )
8602+ import builtins
8603+
8604+ from test import support
8605+ with support .swap_attr (builtins , "int" , dict ):
8606+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("int" )), dict )
8607+
8608+ def test_nested_strings (self ):
8609+ # This variable must have a different name TypeVar
8610+ Tx = TypeVar ("Tx" )
8611+
8612+ class Y (Generic [Tx ]):
8613+ a = "X"
8614+ bT = "Y[T_nonlocal]"
8615+
8616+ Z = TypeAliasType ("Z" , Y [Tx ], type_params = (Tx ,))
8617+
8618+ evaluated_ref1a = evaluate_forward_ref (typing .ForwardRef ("Y[Y['Tx']]" ), locals = {"Y" : Y , "Tx" : Tx })
8619+ self .assertEqual (get_origin (evaluated_ref1a ), Y )
8620+ self .assertEqual (get_args (evaluated_ref1a ), (Y [Tx ],))
8621+
8622+ evaluated_ref1b = evaluate_forward_ref (
8623+ typing .ForwardRef ("Y[Y['Tx']]" ), locals = {"Y" : Y }, type_params = (Tx ,)
8624+ )
8625+ self .assertEqual (get_origin (evaluated_ref1b ), Y )
8626+ self .assertEqual (get_args (evaluated_ref1b ), (Y [Tx ],))
8627+
8628+ with self .subTest ("nested string of TypeVar" ):
8629+ evaluated_ref2 = evaluate_forward_ref (typing .ForwardRef ("""Y["Y['Tx']"]""" ), locals = {"Y" : Y })
8630+ self .assertEqual (get_origin (evaluated_ref2 ), Y )
8631+ if not TYPING_3_9_0 :
8632+ self .skipTest ("Nested string 'Tx' stays ForwardRef in 3.8" )
8633+ self .assertEqual (get_args (evaluated_ref2 ), (Y [Tx ],))
8634+
8635+ with self .subTest ("nested string of TypeAliasType and alias" ):
8636+ # NOTE: Using Y here works for 3.10
8637+ evaluated_ref3 = evaluate_forward_ref (typing .ForwardRef ("""Y['Z["StrAlias"]']""" ), locals = {"Y" : Y , "Z" : Z , "StrAlias" : str })
8638+ self .assertEqual (get_origin (evaluated_ref3 ), Y )
8639+ if sys .version_info [:2 ] in ((3 ,8 ), (3 , 10 )):
8640+ self .skipTest ("Nested string 'StrAlias' is not resolved in 3.8 and 3.10" )
8641+ self .assertEqual (get_args (evaluated_ref3 ), (Z [str ],))
8642+
8643+ def test_invalid_special_forms (self ):
8644+ # tests _lax_type_check to raise errors the same way as the typing module.
8645+ # Regex capture "< class 'module.name'> and "module.name"
8646+ with self .assertRaisesRegex (
8647+ TypeError , r"Plain .*Protocol('>)? is not valid as type argument"
8648+ ):
8649+ evaluate_forward_ref (typing .ForwardRef ("Protocol" ), globals = vars (typing ))
8650+ with self .assertRaisesRegex (
8651+ TypeError , r"Plain .*Generic('>)? is not valid as type argument"
8652+ ):
8653+ evaluate_forward_ref (typing .ForwardRef ("Generic" ), globals = vars (typing ))
8654+ with self .assertRaisesRegex (TypeError , r"Plain typing(_extensions)?\.Final is not valid as type argument" ):
8655+ evaluate_forward_ref (typing .ForwardRef ("Final" ), globals = vars (typing ))
8656+ with self .assertRaisesRegex (TypeError , r"Plain typing(_extensions)?\.ClassVar is not valid as type argument" ):
8657+ evaluate_forward_ref (typing .ForwardRef ("ClassVar" ), globals = vars (typing ))
8658+ if _FORWARD_REF_HAS_CLASS :
8659+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("Final" , is_class = True ), globals = vars (typing )), Final )
8660+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("ClassVar" , is_class = True ), globals = vars (typing )), ClassVar )
8661+ with self .assertRaisesRegex (TypeError , r"Plain typing(_extensions)?\.Final is not valid as type argument" ):
8662+ evaluate_forward_ref (typing .ForwardRef ("Final" , is_argument = False ), globals = vars (typing ))
8663+ with self .assertRaisesRegex (TypeError , r"Plain typing(_extensions)?\.ClassVar is not valid as type argument" ):
8664+ evaluate_forward_ref (typing .ForwardRef ("ClassVar" , is_argument = False ), globals = vars (typing ))
8665+ else :
8666+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("Final" , is_argument = False ), globals = vars (typing )), Final )
8667+ self .assertIs (evaluate_forward_ref (typing .ForwardRef ("ClassVar" , is_argument = False ), globals = vars (typing )), ClassVar )
8668+
84698669
84708670if __name__ == '__main__' :
84718671 main ()
0 commit comments