@@ -113,7 +113,7 @@ def evaluate(
113113 """
114114 match format :
115115 case Format .STRING :
116- return self .__forward_arg__
116+ return self .__resolved_forward_str__
117117 case Format .VALUE :
118118 is_forwardref_format = False
119119 case Format .FORWARDREF :
@@ -258,6 +258,25 @@ def __forward_arg__(self):
258258 "Attempted to access '__forward_arg__' on an uninitialized ForwardRef"
259259 )
260260
261+ @property
262+ def __resolved_forward_str__ (self ):
263+ # __forward_arg__ but with __extra_names__ resolved as strings
264+ resolved_str = self .__forward_arg__
265+ names = self .__extra_names__
266+
267+ if names :
268+ # identifiers can be replaced directly
269+ if resolved_str .isidentifier ():
270+ if (name_obj := names .get (resolved_str ), _sentinel ) is not _sentinel :
271+ resolved_str = type_repr (name_obj )
272+ else :
273+ visitor = _ExtraNameFixer (names )
274+ ast_expr = ast .parse (resolved_str , mode = "eval" ).body
275+ node = visitor .visit (ast_expr )
276+ resolved_str = ast .unparse (node )
277+
278+ return resolved_str
279+
261280 @property
262281 def __forward_code__ (self ):
263282 if self .__code__ is not None :
@@ -321,7 +340,7 @@ def __repr__(self):
321340 extra .append (", is_class=True" )
322341 if self .__owner__ is not None :
323342 extra .append (f", owner={ self .__owner__ !r} " )
324- return f"ForwardRef({ self .__forward_arg__ !r} { '' .join (extra )} )"
343+ return f"ForwardRef({ self .__resolved_forward_str__ !r} { '' .join (extra )} )"
325344
326345
327346_Template = type (t "" )
@@ -1163,3 +1182,16 @@ def _get_dunder_annotations(obj):
11631182 if not isinstance (ann , dict ):
11641183 raise ValueError (f"{ obj !r} .__annotations__ is neither a dict nor None" )
11651184 return ann
1185+
1186+
1187+ class _ExtraNameFixer (ast .NodeTransformer ):
1188+ """Fixer for __extra_names__ items in ForwardRef __repr__ and string evaluation"""
1189+ def __init__ (self , extra_names ):
1190+ self .extra_names = extra_names
1191+
1192+ def visit_Name (self , node : ast .Name ):
1193+ if (new_name := self .extra_names .get (node .id , _sentinel )) is not _sentinel :
1194+ new_node = ast .Name (id = type_repr (new_name ))
1195+ ast .copy_location (node , new_node )
1196+ node = new_node
1197+ return node
0 commit comments