@@ -92,11 +92,28 @@ def __init__(
92
92
def __init_subclass__ (cls , / , * args , ** kwds ):
93
93
raise TypeError ("Cannot subclass ForwardRef" )
94
94
95
- def evaluate (self , * , globals = None , locals = None , type_params = None , owner = None ):
95
+ def evaluate (
96
+ self ,
97
+ * ,
98
+ globals = None ,
99
+ locals = None ,
100
+ type_params = None ,
101
+ owner = None ,
102
+ format = Format .VALUE ,
103
+ ):
96
104
"""Evaluate the forward reference and return the value.
97
105
98
106
If the forward reference cannot be evaluated, raise an exception.
99
107
"""
108
+ match format :
109
+ case Format .STRING :
110
+ return self .__forward_arg__
111
+ case Format .VALUE :
112
+ is_forwardref_format = False
113
+ case Format .FORWARDREF :
114
+ is_forwardref_format = True
115
+ case _:
116
+ raise NotImplementedError (format )
100
117
if self .__cell__ is not None :
101
118
try :
102
119
return self .__cell__ .cell_contents
@@ -159,17 +176,36 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
159
176
arg = self .__forward_arg__
160
177
if arg .isidentifier () and not keyword .iskeyword (arg ):
161
178
if arg in locals :
162
- value = locals [arg ]
179
+ return locals [arg ]
163
180
elif arg in globals :
164
- value = globals [arg ]
181
+ return globals [arg ]
165
182
elif hasattr (builtins , arg ):
166
183
return getattr (builtins , arg )
184
+ elif is_forwardref_format :
185
+ return self
167
186
else :
168
187
raise NameError (arg )
169
188
else :
170
189
code = self .__forward_code__
171
- value = eval (code , globals = globals , locals = locals )
172
- return value
190
+ try :
191
+ return eval (code , globals = globals , locals = locals )
192
+ except Exception :
193
+ if not is_forwardref_format :
194
+ raise
195
+ new_locals = _StringifierDict (
196
+ {** builtins .__dict__ , ** locals },
197
+ globals = globals ,
198
+ owner = owner ,
199
+ is_class = self .__forward_is_class__ ,
200
+ format = format ,
201
+ )
202
+ try :
203
+ result = eval (code , globals = globals , locals = new_locals )
204
+ except Exception :
205
+ return self
206
+ else :
207
+ new_locals .transmogrify ()
208
+ return result
173
209
174
210
def _evaluate (self , globalns , localns , type_params = _sentinel , * , recursive_guard ):
175
211
import typing
@@ -546,6 +582,14 @@ def __missing__(self, key):
546
582
self .stringifiers .append (fwdref )
547
583
return fwdref
548
584
585
+ def transmogrify (self ):
586
+ for obj in self .stringifiers :
587
+ obj .__class__ = ForwardRef
588
+ obj .__stringifier_dict__ = None # not needed for ForwardRef
589
+ if isinstance (obj .__ast_node__ , str ):
590
+ obj .__arg__ = obj .__ast_node__
591
+ obj .__ast_node__ = None
592
+
549
593
def create_unique_name (self ):
550
594
name = f"__annotationlib_name_{ self .next_id } __"
551
595
self .next_id += 1
@@ -595,19 +639,10 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
595
639
# convert each of those into a string to get an approximation of the
596
640
# original source.
597
641
globals = _StringifierDict ({}, format = format )
598
- if annotate .__closure__ :
599
- freevars = annotate .__code__ .co_freevars
600
- new_closure = []
601
- for i , cell in enumerate (annotate .__closure__ ):
602
- if i < len (freevars ):
603
- name = freevars [i ]
604
- else :
605
- name = "__cell__"
606
- fwdref = _Stringifier (name , stringifier_dict = globals )
607
- new_closure .append (types .CellType (fwdref ))
608
- closure = tuple (new_closure )
609
- else :
610
- closure = None
642
+ is_class = isinstance (owner , type )
643
+ closure = _build_closure (
644
+ annotate , owner , is_class , globals , allow_evaluation = False
645
+ )
611
646
func = types .FunctionType (
612
647
annotate .__code__ ,
613
648
globals ,
@@ -649,32 +684,36 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
649
684
is_class = is_class ,
650
685
format = format ,
651
686
)
652
- if annotate .__closure__ :
653
- freevars = annotate .__code__ .co_freevars
654
- new_closure = []
655
- for i , cell in enumerate (annotate .__closure__ ):
656
- try :
657
- cell .cell_contents
658
- except ValueError :
659
- if i < len (freevars ):
660
- name = freevars [i ]
661
- else :
662
- name = "__cell__"
663
- fwdref = _Stringifier (
664
- name ,
665
- cell = cell ,
666
- owner = owner ,
667
- globals = annotate .__globals__ ,
668
- is_class = is_class ,
669
- stringifier_dict = globals ,
670
- )
671
- globals .stringifiers .append (fwdref )
672
- new_closure .append (types .CellType (fwdref ))
673
- else :
674
- new_closure .append (cell )
675
- closure = tuple (new_closure )
687
+ closure = _build_closure (
688
+ annotate , owner , is_class , globals , allow_evaluation = True
689
+ )
690
+ func = types .FunctionType (
691
+ annotate .__code__ ,
692
+ globals ,
693
+ closure = closure ,
694
+ argdefs = annotate .__defaults__ ,
695
+ kwdefaults = annotate .__kwdefaults__ ,
696
+ )
697
+ try :
698
+ result = func (Format .VALUE_WITH_FAKE_GLOBALS )
699
+ except Exception :
700
+ pass
676
701
else :
677
- closure = None
702
+ globals .transmogrify ()
703
+ return result
704
+
705
+ # Try again, but do not provide any globals. This allows us to return
706
+ # a value in certain cases where an exception gets raised during evaluation.
707
+ globals = _StringifierDict (
708
+ {},
709
+ globals = annotate .__globals__ ,
710
+ owner = owner ,
711
+ is_class = is_class ,
712
+ format = format ,
713
+ )
714
+ closure = _build_closure (
715
+ annotate , owner , is_class , globals , allow_evaluation = False
716
+ )
678
717
func = types .FunctionType (
679
718
annotate .__code__ ,
680
719
globals ,
@@ -683,13 +722,21 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
683
722
kwdefaults = annotate .__kwdefaults__ ,
684
723
)
685
724
result = func (Format .VALUE_WITH_FAKE_GLOBALS )
686
- for obj in globals .stringifiers :
687
- obj .__class__ = ForwardRef
688
- obj .__stringifier_dict__ = None # not needed for ForwardRef
689
- if isinstance (obj .__ast_node__ , str ):
690
- obj .__arg__ = obj .__ast_node__
691
- obj .__ast_node__ = None
692
- return result
725
+ globals .transmogrify ()
726
+ if _is_evaluate :
727
+ if isinstance (result , ForwardRef ):
728
+ return result .evaluate (format = Format .FORWARDREF )
729
+ else :
730
+ return result
731
+ else :
732
+ return {
733
+ key : (
734
+ val .evaluate (format = Format .FORWARDREF )
735
+ if isinstance (val , ForwardRef )
736
+ else val
737
+ )
738
+ for key , val in result .items ()
739
+ }
693
740
elif format == Format .VALUE :
694
741
# Should be impossible because __annotate__ functions must not raise
695
742
# NotImplementedError for this format.
@@ -698,6 +745,39 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
698
745
raise ValueError (f"Invalid format: { format !r} " )
699
746
700
747
748
+ def _build_closure (annotate , owner , is_class , stringifier_dict , * , allow_evaluation ):
749
+ if not annotate .__closure__ :
750
+ return None
751
+ freevars = annotate .__code__ .co_freevars
752
+ new_closure = []
753
+ for i , cell in enumerate (annotate .__closure__ ):
754
+ if i < len (freevars ):
755
+ name = freevars [i ]
756
+ else :
757
+ name = "__cell__"
758
+ new_cell = None
759
+ if allow_evaluation :
760
+ try :
761
+ cell .cell_contents
762
+ except ValueError :
763
+ pass
764
+ else :
765
+ new_cell = cell
766
+ if new_cell is None :
767
+ fwdref = _Stringifier (
768
+ name ,
769
+ cell = cell ,
770
+ owner = owner ,
771
+ globals = annotate .__globals__ ,
772
+ is_class = is_class ,
773
+ stringifier_dict = stringifier_dict ,
774
+ )
775
+ stringifier_dict .stringifiers .append (fwdref )
776
+ new_cell = types .CellType (fwdref )
777
+ new_closure .append (new_cell )
778
+ return tuple (new_closure )
779
+
780
+
701
781
def _stringify_single (anno ):
702
782
if anno is ...:
703
783
return "..."
@@ -809,7 +889,7 @@ def get_annotations(
809
889
# But if we didn't get it, we use __annotations__ instead.
810
890
ann = _get_dunder_annotations (obj )
811
891
if ann is not None :
812
- return annotations_to_string (ann )
892
+ return annotations_to_string (ann )
813
893
case Format .VALUE_WITH_FAKE_GLOBALS :
814
894
raise ValueError ("The VALUE_WITH_FAKE_GLOBALS format is for internal use only" )
815
895
case _:
0 commit comments