@@ -92,11 +92,28 @@ def __init__(
9292 def __init_subclass__ (cls , / , * args , ** kwds ):
9393 raise TypeError ("Cannot subclass ForwardRef" )
9494
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+ ):
96104 """Evaluate the forward reference and return the value.
97105
98106 If the forward reference cannot be evaluated, raise an exception.
99107 """
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 )
100117 if self .__cell__ is not None :
101118 try :
102119 return self .__cell__ .cell_contents
@@ -159,17 +176,36 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
159176 arg = self .__forward_arg__
160177 if arg .isidentifier () and not keyword .iskeyword (arg ):
161178 if arg in locals :
162- value = locals [arg ]
179+ return locals [arg ]
163180 elif arg in globals :
164- value = globals [arg ]
181+ return globals [arg ]
165182 elif hasattr (builtins , arg ):
166183 return getattr (builtins , arg )
184+ elif is_forwardref_format :
185+ return self
167186 else :
168187 raise NameError (arg )
169188 else :
170189 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
173209
174210 def _evaluate (self , globalns , localns , type_params = _sentinel , * , recursive_guard ):
175211 import typing
@@ -546,6 +582,14 @@ def __missing__(self, key):
546582 self .stringifiers .append (fwdref )
547583 return fwdref
548584
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+
549593 def create_unique_name (self ):
550594 name = f"__annotationlib_name_{ self .next_id } __"
551595 self .next_id += 1
@@ -595,19 +639,10 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
595639 # convert each of those into a string to get an approximation of the
596640 # original source.
597641 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+ )
611646 func = types .FunctionType (
612647 annotate .__code__ ,
613648 globals ,
@@ -649,32 +684,36 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
649684 is_class = is_class ,
650685 format = format ,
651686 )
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
676701 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+ )
678717 func = types .FunctionType (
679718 annotate .__code__ ,
680719 globals ,
@@ -683,13 +722,21 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
683722 kwdefaults = annotate .__kwdefaults__ ,
684723 )
685724 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+ }
693740 elif format == Format .VALUE :
694741 # Should be impossible because __annotate__ functions must not raise
695742 # NotImplementedError for this format.
@@ -698,6 +745,39 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
698745 raise ValueError (f"Invalid format: { format !r} " )
699746
700747
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+
701781def _stringify_single (anno ):
702782 if anno is ...:
703783 return "..."
@@ -809,7 +889,7 @@ def get_annotations(
809889 # But if we didn't get it, we use __annotations__ instead.
810890 ann = _get_dunder_annotations (obj )
811891 if ann is not None :
812- return annotations_to_string (ann )
892+ return annotations_to_string (ann )
813893 case Format .VALUE_WITH_FAKE_GLOBALS :
814894 raise ValueError ("The VALUE_WITH_FAKE_GLOBALS format is for internal use only" )
815895 case _:
0 commit comments