@@ -573,21 +573,11 @@ def _write_str_avoiding_backslashes(self, string, *, quote_types=_ALL_QUOTES):
573573 quote_type = quote_types [0 ]
574574 self .write (f"{ quote_type } { string } { quote_type } " )
575575
576- def visit_JoinedStr (self , node ):
577- self .write ("f" )
578-
579- fstring_parts = []
580- for value in node .values :
581- with self .buffered () as buffer :
582- self ._write_fstring_inner (value )
583- fstring_parts .append (
584- ("" .join (buffer ), isinstance (value , Constant ))
585- )
586-
587- new_fstring_parts = []
576+ def _ftstring_helper (self , node , ftstring_parts ):
577+ new_ftstring_parts = []
588578 quote_types = list (_ALL_QUOTES )
589579 fallback_to_repr = False
590- for value , is_constant in fstring_parts :
580+ for value , is_constant in ftstring_parts :
591581 if is_constant :
592582 value , new_quote_types = self ._str_literal_helper (
593583 value ,
@@ -606,30 +596,47 @@ def visit_JoinedStr(self, node):
606596 new_quote_types = [q for q in quote_types if q not in value ]
607597 if new_quote_types :
608598 quote_types = new_quote_types
609- new_fstring_parts .append (value )
599+ new_ftstring_parts .append (value )
610600
611601 if fallback_to_repr :
612602 # If we weren't able to find a quote type that works for all parts
613603 # of the JoinedStr, fallback to using repr and triple single quotes.
614604 quote_types = ["'''" ]
615- new_fstring_parts .clear ()
616- for value , is_constant in fstring_parts :
605+ new_ftstring_parts .clear ()
606+ for value , is_constant in ftstring_parts :
617607 if is_constant :
618608 value = repr ('"' + value ) # force repr to use single quotes
619609 expected_prefix = "'\" "
620610 assert value .startswith (expected_prefix ), repr (value )
621611 value = value [len (expected_prefix ):- 1 ]
622- new_fstring_parts .append (value )
612+ new_ftstring_parts .append (value )
623613
624- value = "" .join (new_fstring_parts )
614+ value = "" .join (new_ftstring_parts )
625615 quote_type = quote_types [0 ]
626616 self .write (f"{ quote_type } { value } { quote_type } " )
627617
628- def _write_fstring_inner (self , node , is_format_spec = False ):
618+ def _write_ftstring (self , node , prefix ):
619+ self .write (prefix )
620+ fstring_parts = []
621+ for value in node .values :
622+ with self .buffered () as buffer :
623+ self ._write_ftstring_inner (value )
624+ fstring_parts .append (
625+ ("" .join (buffer ), isinstance (value , Constant ))
626+ )
627+ self ._ftstring_helper (node , fstring_parts )
628+
629+ def visit_JoinedStr (self , node ):
630+ self ._write_ftstring (node , "f" )
631+
632+ def visit_TemplateStr (self , node ):
633+ self ._write_ftstring (node , "t" )
634+
635+ def _write_ftstring_inner (self , node , is_format_spec = False ):
629636 if isinstance (node , JoinedStr ):
630637 # for both the f-string itself, and format_spec
631638 for value in node .values :
632- self ._write_fstring_inner (value , is_format_spec = is_format_spec )
639+ self ._write_ftstring_inner (value , is_format_spec = is_format_spec )
633640 elif isinstance (node , Constant ) and isinstance (node .value , str ):
634641 value = node .value .replace ("{" , "{{" ).replace ("}" , "}}" )
635642
@@ -641,26 +648,41 @@ def _write_fstring_inner(self, node, is_format_spec=False):
641648 self .write (value )
642649 elif isinstance (node , FormattedValue ):
643650 self .visit_FormattedValue (node )
651+ elif isinstance (node , Interpolation ):
652+ self .visit_Interpolation (node )
644653 else :
645654 raise ValueError (f"Unexpected node inside JoinedStr, { node !r} " )
646655
647- def visit_FormattedValue (self , node ):
648- def unparse_inner (inner ):
649- unparser = type (self )()
650- unparser .set_precedence (_Precedence .TEST .next (), inner )
651- return unparser .visit (inner )
656+ def _unparse_interpolation_value (self , inner ):
657+ unparser = type (self )()
658+ unparser .set_precedence (_Precedence .TEST .next (), inner )
659+ return unparser .visit (inner )
660+
661+ def _write_fstring_conversion (self , node ):
662+ if node .conversion != - 1 :
663+ self .write (f"!{ chr (node .conversion )} " )
664+
665+ def _write_tstring_conversion (self , node ):
666+ if node .conversion is not None :
667+ self .write (f"!{ node .conversion } " )
652668
669+ def _write_interpolation (self , node , write_conversion ):
653670 with self .delimit ("{" , "}" ):
654- expr = unparse_inner (node .value )
671+ expr = self . _unparse_interpolation_value (node .value )
655672 if expr .startswith ("{" ):
656673 # Separate pair of opening brackets as "{ {"
657674 self .write (" " )
658675 self .write (expr )
659- if node .conversion != - 1 :
660- self .write (f"!{ chr (node .conversion )} " )
676+ write_conversion (node )
661677 if node .format_spec :
662678 self .write (":" )
663- self ._write_fstring_inner (node .format_spec , is_format_spec = True )
679+ self ._write_ftstring_inner (node .format_spec , is_format_spec = True )
680+
681+ def visit_FormattedValue (self , node ):
682+ self ._write_interpolation (node , self ._write_fstring_conversion )
683+
684+ def visit_Interpolation (self , node ):
685+ self ._write_interpolation (node , self ._write_tstring_conversion )
664686
665687 def visit_Name (self , node ):
666688 self .write (node .id )
0 commit comments