28
28
import typing_extensions
29
29
from _typed_dict_test_helper import Foo , FooGeneric , VeryAnnotated
30
30
from typing_extensions import (
31
+ _FORWARD_REF_HAS_CLASS ,
31
32
_PEP_649_OR_749_IMPLEMENTED ,
32
33
Annotated ,
33
34
Any ,
82
83
clear_overloads ,
83
84
dataclass_transform ,
84
85
deprecated ,
86
+ evaluate_forward_ref ,
85
87
final ,
86
88
get_annotations ,
87
89
get_args ,
@@ -7948,7 +7950,7 @@ def f2(a: "undefined"): # noqa: F821
7948
7950
self .assertEqual (get_annotations (f2 , format = 2 ), {"a" : "undefined" })
7949
7951
7950
7952
self .assertEqual (
7951
- get_annotations (f1 , format = Format .SOURCE ),
7953
+ get_annotations (f1 , format = Format .STRING ),
7952
7954
{"a" : "int" },
7953
7955
)
7954
7956
self .assertEqual (get_annotations (f1 , format = 3 ), {"a" : "int" })
@@ -7975,7 +7977,7 @@ def foo():
7975
7977
foo , format = Format .FORWARDREF , eval_str = True
7976
7978
)
7977
7979
get_annotations (
7978
- foo , format = Format .SOURCE , eval_str = True
7980
+ foo , format = Format .STRING , eval_str = True
7979
7981
)
7980
7982
7981
7983
def test_stock_annotations (self ):
@@ -7989,7 +7991,7 @@ def foo(a: int, b: str):
7989
7991
{"a" : int , "b" : str },
7990
7992
)
7991
7993
self .assertEqual (
7992
- get_annotations (foo , format = Format .SOURCE ),
7994
+ get_annotations (foo , format = Format .STRING ),
7993
7995
{"a" : "int" , "b" : "str" },
7994
7996
)
7995
7997
@@ -8084,43 +8086,43 @@ def test_stock_annotations_in_module(self):
8084
8086
)
8085
8087
8086
8088
self .assertEqual (
8087
- get_annotations (isa , format = Format .SOURCE ),
8089
+ get_annotations (isa , format = Format .STRING ),
8088
8090
{"a" : "int" , "b" : "str" },
8089
8091
)
8090
8092
self .assertEqual (
8091
- get_annotations (isa .MyClass , format = Format .SOURCE ),
8093
+ get_annotations (isa .MyClass , format = Format .STRING ),
8092
8094
{"a" : "int" , "b" : "str" },
8093
8095
)
8094
8096
mycls = "MyClass" if _PEP_649_OR_749_IMPLEMENTED else "inspect_stock_annotations.MyClass"
8095
8097
self .assertEqual (
8096
- get_annotations (isa .function , format = Format .SOURCE ),
8098
+ get_annotations (isa .function , format = Format .STRING ),
8097
8099
{"a" : "int" , "b" : "str" , "return" : mycls },
8098
8100
)
8099
8101
self .assertEqual (
8100
8102
get_annotations (
8101
- isa .function2 , format = Format .SOURCE
8103
+ isa .function2 , format = Format .STRING
8102
8104
),
8103
8105
{"a" : "int" , "b" : "str" , "c" : mycls , "return" : mycls },
8104
8106
)
8105
8107
self .assertEqual (
8106
8108
get_annotations (
8107
- isa .function3 , format = Format .SOURCE
8109
+ isa .function3 , format = Format .STRING
8108
8110
),
8109
8111
{"a" : "int" , "b" : "str" , "c" : "MyClass" },
8110
8112
)
8111
8113
self .assertEqual (
8112
- get_annotations (inspect , format = Format .SOURCE ),
8114
+ get_annotations (inspect , format = Format .STRING ),
8113
8115
{},
8114
8116
)
8115
8117
self .assertEqual (
8116
8118
get_annotations (
8117
- isa .UnannotatedClass , format = Format .SOURCE
8119
+ isa .UnannotatedClass , format = Format .STRING
8118
8120
),
8119
8121
{},
8120
8122
)
8121
8123
self .assertEqual (
8122
8124
get_annotations (
8123
- isa .unannotated_function , format = Format .SOURCE
8125
+ isa .unannotated_function , format = Format .STRING
8124
8126
),
8125
8127
{},
8126
8128
)
@@ -8141,7 +8143,7 @@ def test_stock_annotations_on_wrapper(self):
8141
8143
)
8142
8144
mycls = "MyClass" if _PEP_649_OR_749_IMPLEMENTED else "inspect_stock_annotations.MyClass"
8143
8145
self .assertEqual (
8144
- get_annotations (wrapped , format = Format .SOURCE ),
8146
+ get_annotations (wrapped , format = Format .STRING ),
8145
8147
{"a" : "int" , "b" : "str" , "return" : mycls },
8146
8148
)
8147
8149
self .assertEqual (
@@ -8160,10 +8162,10 @@ def test_stringized_annotations_in_module(self):
8160
8162
{"eval_str" : False },
8161
8163
{"format" : Format .VALUE },
8162
8164
{"format" : Format .FORWARDREF },
8163
- {"format" : Format .SOURCE },
8165
+ {"format" : Format .STRING },
8164
8166
{"format" : Format .VALUE , "eval_str" : False },
8165
8167
{"format" : Format .FORWARDREF , "eval_str" : False },
8166
- {"format" : Format .SOURCE , "eval_str" : False },
8168
+ {"format" : Format .STRING , "eval_str" : False },
8167
8169
]:
8168
8170
with self .subTest (** kwargs ):
8169
8171
self .assertEqual (
@@ -8466,6 +8468,204 @@ def test_pep_695_generics_with_future_annotations_nested_in_function(self):
8466
8468
set (results .generic_func .__type_params__ )
8467
8469
)
8468
8470
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
+
8469
8669
8470
8670
if __name__ == '__main__' :
8471
8671
main ()
0 commit comments