@@ -90,11 +90,21 @@ def __init__(
9090 def __init_subclass__ (cls , / , * args , ** kwds ):
9191 raise TypeError ("Cannot subclass ForwardRef" )
9292
93- def evaluate (self , * , globals = None , locals = None , type_params = None , owner = None ):
93+ def evaluate (self , * , globals = None , locals = None , type_params = None , owner = None ,
94+ format = Format .VALUE ):
9495 """Evaluate the forward reference and return the value.
9596
9697 If the forward reference cannot be evaluated, raise an exception.
9798 """
99+ match format :
100+ case Format .STRING :
101+ return self .__forward_arg__
102+ case Format .VALUE :
103+ is_forwardref_format = False
104+ case Format .FORWARDREF :
105+ is_forwardref_format = True
106+ case _:
107+ raise NotImplementedError (format )
98108 if self .__cell__ is not None :
99109 try :
100110 return self .__cell__ .cell_contents
@@ -155,17 +165,33 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
155165 arg = self .__forward_arg__
156166 if arg .isidentifier () and not keyword .iskeyword (arg ):
157167 if arg in locals :
158- value = locals [arg ]
168+ return locals [arg ]
159169 elif arg in globals :
160- value = globals [arg ]
170+ return globals [arg ]
161171 elif hasattr (builtins , arg ):
162172 return getattr (builtins , arg )
173+ elif is_forwardref_format :
174+ return self
163175 else :
164176 raise NameError (arg )
165177 else :
166178 code = self .__forward_code__
167- value = eval (code , globals = globals , locals = locals )
168- return value
179+ try :
180+ return eval (code , globals = globals , locals = locals )
181+ except Exception :
182+ if not is_forwardref_format :
183+ raise
184+ new_locals = _StringifierDict (
185+ {** builtins .__dict__ , ** locals }, globals = globals , owner = owner ,
186+ is_class = self .__forward_is_class__
187+ )
188+ try :
189+ result = eval (code , globals = globals , locals = new_locals )
190+ except Exception :
191+ return self
192+ else :
193+ new_locals .transmogrify ()
194+ return result
169195
170196 def _evaluate (self , globalns , localns , type_params = _sentinel , * , recursive_guard ):
171197 import typing
@@ -478,6 +504,14 @@ def __missing__(self, key):
478504 self .stringifiers .append (fwdref )
479505 return fwdref
480506
507+ def transmogrify (self ):
508+ for obj in self .stringifiers :
509+ obj .__class__ = ForwardRef
510+ obj .__stringifier_dict__ = None # not needed for ForwardRef
511+ if isinstance (obj .__ast_node__ , str ):
512+ obj .__arg__ = obj .__ast_node__
513+ obj .__ast_node__ = None
514+
481515
482516def call_evaluate_function (evaluate , format , * , owner = None ):
483517 """Call an evaluate function. Evaluate functions are normally generated for
@@ -522,19 +556,10 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
522556 # convert each of those into a string to get an approximation of the
523557 # original source.
524558 globals = _StringifierDict ({})
525- if annotate .__closure__ :
526- freevars = annotate .__code__ .co_freevars
527- new_closure = []
528- for i , cell in enumerate (annotate .__closure__ ):
529- if i < len (freevars ):
530- name = freevars [i ]
531- else :
532- name = "__cell__"
533- fwdref = _Stringifier (name , stringifier_dict = globals )
534- new_closure .append (types .CellType (fwdref ))
535- closure = tuple (new_closure )
536- else :
537- closure = None
559+ is_class = isinstance (owner , type )
560+ closure = _build_closure (
561+ annotate , owner , is_class , globals , allow_evaluation = False
562+ )
538563 func = types .FunctionType (
539564 annotate .__code__ ,
540565 globals ,
@@ -570,32 +595,30 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
570595 namespace = {** annotate .__builtins__ , ** annotate .__globals__ }
571596 is_class = isinstance (owner , type )
572597 globals = _StringifierDict (namespace , annotate .__globals__ , owner , is_class )
573- if annotate .__closure__ :
574- freevars = annotate .__code__ .co_freevars
575- new_closure = []
576- for i , cell in enumerate (annotate .__closure__ ):
577- try :
578- cell .cell_contents
579- except ValueError :
580- if i < len (freevars ):
581- name = freevars [i ]
582- else :
583- name = "__cell__"
584- fwdref = _Stringifier (
585- name ,
586- cell = cell ,
587- owner = owner ,
588- globals = annotate .__globals__ ,
589- is_class = is_class ,
590- stringifier_dict = globals ,
591- )
592- globals .stringifiers .append (fwdref )
593- new_closure .append (types .CellType (fwdref ))
594- else :
595- new_closure .append (cell )
596- closure = tuple (new_closure )
598+ closure = _build_closure (
599+ annotate , owner , is_class , globals , allow_evaluation = True
600+ )
601+ func = types .FunctionType (
602+ annotate .__code__ ,
603+ globals ,
604+ closure = closure ,
605+ argdefs = annotate .__defaults__ ,
606+ kwdefaults = annotate .__kwdefaults__ ,
607+ )
608+ try :
609+ result = func (Format .VALUE_WITH_FAKE_GLOBALS )
610+ except Exception :
611+ pass
597612 else :
598- closure = None
613+ globals .transmogrify ()
614+ return result
615+
616+ # Try again, but do not provide any globals. This allows us to return
617+ # a value in certain cases where an exception gets raised during evaluation.
618+ globals = _StringifierDict ({}, annotate .__globals__ , owner , is_class )
619+ closure = _build_closure (
620+ annotate , owner , is_class , globals , allow_evaluation = False
621+ )
599622 func = types .FunctionType (
600623 annotate .__code__ ,
601624 globals ,
@@ -604,13 +627,21 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
604627 kwdefaults = annotate .__kwdefaults__ ,
605628 )
606629 result = func (Format .VALUE_WITH_FAKE_GLOBALS )
607- for obj in globals .stringifiers :
608- obj .__class__ = ForwardRef
609- obj .__stringifier_dict__ = None # not needed for ForwardRef
610- if isinstance (obj .__ast_node__ , str ):
611- obj .__arg__ = obj .__ast_node__
612- obj .__ast_node__ = None
613- return result
630+ globals .transmogrify ()
631+ if _is_evaluate :
632+ if isinstance (result , ForwardRef ):
633+ return result .evaluate (format = Format .FORWARDREF )
634+ else :
635+ return result
636+ else :
637+ return {
638+ key : (
639+ val .evaluate (format = Format .FORWARDREF )
640+ if isinstance (val , ForwardRef )
641+ else val
642+ )
643+ for key , val in result .items ()
644+ }
614645 elif format == Format .VALUE :
615646 # Should be impossible because __annotate__ functions must not raise
616647 # NotImplementedError for this format.
@@ -619,6 +650,40 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
619650 raise ValueError (f"Invalid format: { format !r} " )
620651
621652
653+ def _build_closure (annotate , owner , is_class , stringifier_dict , * ,
654+ allow_evaluation ):
655+ if not annotate .__closure__ :
656+ return None
657+ freevars = annotate .__code__ .co_freevars
658+ new_closure = []
659+ for i , cell in enumerate (annotate .__closure__ ):
660+ if i < len (freevars ):
661+ name = freevars [i ]
662+ else :
663+ name = "__cell__"
664+ new_cell = None
665+ if allow_evaluation :
666+ try :
667+ cell .cell_contents
668+ except ValueError :
669+ pass
670+ else :
671+ new_cell = cell
672+ if new_cell is None :
673+ fwdref = _Stringifier (
674+ name ,
675+ cell = cell ,
676+ owner = owner ,
677+ globals = annotate .__globals__ ,
678+ is_class = is_class ,
679+ stringifier_dict = globals ,
680+ )
681+ stringifier_dict .stringifiers .append (fwdref )
682+ new_cell = types .CellType (fwdref )
683+ new_closure .append (new_cell )
684+ return tuple (new_closure )
685+
686+
622687def get_annotate_function (obj ):
623688 """Get the __annotate__ function for an object.
624689
0 commit comments