@@ -560,32 +560,70 @@ def unary_op(self):
560560    del  _make_unary_op 
561561
562562
563- def  _template_to_ast (template ):
563+ def  _template_to_ast_constructor (template ):
564+     """Convert a `template` instance to a non-literal AST.""" 
565+     args  =  []
566+     for  part  in  template :
567+         match  part :
568+             case  str ():
569+                 args .append (ast .Constant (value = part ))
570+             case  _:
571+                 interp  =  ast .Call (
572+                     func = ast .Name (id = "Interpolation" ),
573+                     args = [
574+                         ast .Constant (value = part .value ),
575+                         ast .Constant (value = part .expression ),
576+                         ast .Constant (value = part .conversion ),
577+                         ast .Constant (value = part .format_spec ),
578+                     ]
579+                 )
580+                 args .append (interp )
581+     return  ast .Call (func = ast .Name (id = "Template" ), args = args , keywords = [])
582+ 
583+ 
584+ def  _template_to_ast_literal (template , parsed ):
585+     """Convert a `template` instance to a t-string literal AST.""" 
564586    values  =  []
587+     interp_count  =  0 
565588    for  part  in  template :
566589        match  part :
567590            case  str ():
568591                values .append (ast .Constant (value = part ))
569-             # Interpolation, but we don't want to import the string module 
570592            case  _:
571593                interp  =  ast .Interpolation (
572594                    str = part .expression ,
573-                     value = ast .parse (part .expression ),
574-                     conversion = (
575-                         ord (part .conversion )
576-                         if  part .conversion  is  not   None 
577-                         else  - 1 
578-                     ),
579-                     format_spec = (
580-                         ast .Constant (value = part .format_spec )
581-                         if  part .format_spec  !=  "" 
582-                         else  None 
583-                     ),
595+                     value = parsed [interp_count ],
596+                     conversion = ord (part .conversion ) if  part .conversion  else  - 1 ,
597+                     format_spec = ast .Constant (value = part .format_spec )
598+                     if  part .format_spec 
599+                     else  None ,
584600                )
585601                values .append (interp )
602+                 interp_count  +=  1 
586603    return  ast .TemplateStr (values = values )
587604
588605
606+ def  _template_to_ast (template ):
607+     """Make a best-effort conversion of a `template` instance to an AST.""" 
608+     # gh-138558: Not all Template instances can be represented as t-string 
609+     # literals. Return the most accurate AST we can. See issue for details. 
610+ 
611+     # If any expr is empty or whitespace only, we cannot convert to a literal. 
612+     if  any (part .expression .strip () ==  ""  for  part  in  template .interpolations ):
613+         return  _template_to_ast_constructor (template )
614+ 
615+     try :
616+         # Wrap in parens to allow whitespace inside interpolation curly braces 
617+         parsed  =  tuple (
618+             ast .parse (f"({ part .expression }  )" , mode = "eval" ).body 
619+             for  part  in  template .interpolations 
620+         )
621+     except  SyntaxError :
622+         return  _template_to_ast_constructor (template )
623+ 
624+     return  _template_to_ast_literal (template , parsed )
625+ 
626+ 
589627class  _StringifierDict (dict ):
590628    def  __init__ (self , namespace , * , globals = None , owner = None , is_class = False , format ):
591629        super ().__init__ (namespace )
0 commit comments