@@ -24,12 +24,12 @@ class FString(object):
2424 An F-string in the expression part of another f-string
2525 """
2626
27- def __init__ (self , node , allowed_quotes , quote_reuse ):
27+ def __init__ (self , node , allowed_quotes , pep701 ):
2828 assert isinstance (node , ast .JoinedStr )
2929
3030 self .node = node
3131 self .allowed_quotes = allowed_quotes
32- self .quote_reuse = quote_reuse
32+ self .pep701 = pep701
3333
3434 def is_correct_ast (self , code ):
3535 try :
@@ -54,7 +54,7 @@ def complete_debug_specifier(self, partial_specifier_candidates, value_node):
5454 conversion_candidates = [x + conversion for x in partial_specifier_candidates ]
5555
5656 if value_node .format_spec is not None :
57- conversion_candidates = [c + ':' + fs for c in conversion_candidates for fs in FormatSpec (value_node .format_spec , self .allowed_quotes , self .quote_reuse ).candidates ()]
57+ conversion_candidates = [c + ':' + fs for c in conversion_candidates for fs in FormatSpec (value_node .format_spec , self .allowed_quotes , self .pep701 ).candidates ()]
5858
5959 return [x + '}' for x in conversion_candidates ]
6060
@@ -66,7 +66,7 @@ def candidates(self):
6666 debug_specifier_candidates = []
6767 nested_allowed = copy .copy (self .allowed_quotes )
6868
69- if not self .quote_reuse :
69+ if not self .pep701 :
7070 nested_allowed .remove (quote )
7171
7272 for v in self .node .values :
@@ -90,7 +90,7 @@ def candidates(self):
9090 try :
9191 completed = self .complete_debug_specifier (debug_specifier_candidates , v )
9292 candidates = [
93- x + y for x in candidates for y in FormattedValue (v , nested_allowed , self .quote_reuse ).get_candidates ()
93+ x + y for x in candidates for y in FormattedValue (v , nested_allowed , self .pep701 ).get_candidates ()
9494 ] + completed
9595 debug_specifier_candidates = []
9696 except Exception as e :
@@ -115,9 +115,9 @@ class OuterFString(FString):
115115 OuterFString is free to use backslashes in the Str parts
116116 """
117117
118- def __init__ (self , node , quote_reuse = False ):
118+ def __init__ (self , node , pep701 = False ):
119119 assert isinstance (node , ast .JoinedStr )
120- super (OuterFString , self ).__init__ (node , ['"' , "'" , '"""' , "'''" ], quote_reuse = quote_reuse )
120+ super (OuterFString , self ).__init__ (node , ['"' , "'" , '"""' , "'''" ], pep701 = pep701 )
121121
122122 def __str__ (self ):
123123 if len (self .node .values ) == 0 :
@@ -155,13 +155,13 @@ class FormattedValue(ExpressionPrinter):
155155 An F-String Expression Part
156156 """
157157
158- def __init__ (self , node , allowed_quotes , quote_reuse ):
158+ def __init__ (self , node , allowed_quotes , pep701 ):
159159 super (FormattedValue , self ).__init__ ()
160160
161161 assert isinstance (node , ast .FormattedValue )
162162 self .node = node
163163 self .allowed_quotes = allowed_quotes
164- self .quote_reuse = quote_reuse
164+ self .pep701 = pep701
165165 self .candidates = ['' ]
166166
167167 def get_candidates (self ):
@@ -182,7 +182,7 @@ def get_candidates(self):
182182
183183 if self .node .format_spec is not None :
184184 self .printer .delimiter (':' )
185- self ._append (FormatSpec (self .node .format_spec , self .allowed_quotes , quote_reuse = self .quote_reuse ).candidates ())
185+ self ._append (FormatSpec (self .node .format_spec , self .allowed_quotes , pep701 = self .pep701 ).candidates ())
186186
187187 self .printer .delimiter ('}' )
188188
@@ -211,7 +211,7 @@ def is_curly(self, node):
211211 return False
212212
213213 def visit_Str (self , node ):
214- self .printer .append (str (Str (node .s , self .allowed_quotes )), TokenTypes .NonNumberLiteral )
214+ self .printer .append (str (Str (node .s , self .allowed_quotes , self . pep701 )), TokenTypes .NonNumberLiteral )
215215
216216 def visit_Bytes (self , node ):
217217 self .printer .append (str (Bytes (node .s , self .allowed_quotes )), TokenTypes .NonNumberLiteral )
@@ -220,7 +220,7 @@ def visit_JoinedStr(self, node):
220220 assert isinstance (node , ast .JoinedStr )
221221 if self .printer .previous_token in [TokenTypes .Identifier , TokenTypes .Keyword , TokenTypes .SoftKeyword ]:
222222 self .printer .delimiter (' ' )
223- self ._append (FString (node , allowed_quotes = self .allowed_quotes , quote_reuse = self .quote_reuse ).candidates ())
223+ self ._append (FString (node , allowed_quotes = self .allowed_quotes , pep701 = self .pep701 ).candidates ())
224224
225225 def _finalize (self ):
226226 self .candidates = [x + str (self .printer ) for x in self .candidates ]
@@ -235,20 +235,21 @@ class Str(object):
235235 """
236236 A Str node inside an f-string expression
237237
238- May use any of the allowed quotes, no backslashes!
238+ May use any of the allowed quotes. In Python <3.12, backslashes are not allowed.
239239
240240 """
241241
242- def __init__ (self , s , allowed_quotes ):
242+ def __init__ (self , s , allowed_quotes , pep701 = False ):
243243 self ._s = s
244244 self .allowed_quotes = allowed_quotes
245245 self .current_quote = None
246+ self .pep701 = pep701
246247
247248 def _can_quote (self , c ):
248249 if self .current_quote is None :
249250 return False
250251
251- if (c == '\n ' or c == '\r ' ) and len (self .current_quote ) == 1 :
252+ if (c == '\n ' or c == '\r ' ) and len (self .current_quote ) == 1 and not self . pep701 :
252253 return False
253254
254255 if c == self .current_quote [0 ]:
@@ -258,7 +259,7 @@ def _can_quote(self, c):
258259
259260 def _get_quote (self , c ):
260261 for quote in self .allowed_quotes :
261- if c == '\n ' or c == '\r ' :
262+ if not self . pep701 and ( c == '\n ' or c == '\r ' ) :
262263 if len (quote ) == 3 :
263264 return quote
264265 elif c != quote :
@@ -279,7 +280,13 @@ def _literals(self):
279280
280281 if l == '' :
281282 l += self .current_quote
282- l += c
283+
284+ if c == '\n ' :
285+ l += '\\ n'
286+ elif c == '\r ' :
287+ l += '\\ r'
288+ else :
289+ l += c
283290
284291 if l :
285292 l += self .current_quote
@@ -292,7 +299,7 @@ def __str__(self):
292299 if '\0 ' in self ._s or '\\ ' in self ._s :
293300 raise ValueError ('Impossible to represent a %r character in f-string expression part' )
294301
295- if '\n ' in self ._s or '\r ' in self ._s :
302+ if not self . pep701 and ( '\n ' in self ._s or '\r ' in self ._s ) :
296303 if '"""' not in self .allowed_quotes and "'''" not in self .allowed_quotes :
297304 raise ValueError (
298305 'Impossible to represent newline character in f-string expression part without a long quote'
@@ -324,12 +331,12 @@ class FormatSpec(object):
324331
325332 """
326333
327- def __init__ (self , node , allowed_quotes , quote_reuse ):
334+ def __init__ (self , node , allowed_quotes , pep701 ):
328335 assert isinstance (node , ast .JoinedStr )
329336
330337 self .node = node
331338 self .allowed_quotes = allowed_quotes
332- self .quote_reuse = quote_reuse
339+ self .pep701 = pep701
333340
334341 def candidates (self ):
335342
@@ -339,7 +346,7 @@ def candidates(self):
339346 candidates = [x + self .str_for (v .s ) for x in candidates ]
340347 elif isinstance (v , ast .FormattedValue ):
341348 candidates = [
342- x + y for x in candidates for y in FormattedValue (v , self .allowed_quotes , self .quote_reuse ).get_candidates ()
349+ x + y for x in candidates for y in FormattedValue (v , self .allowed_quotes , self .pep701 ).get_candidates ()
343350 ]
344351 else :
345352 raise RuntimeError ('Unexpected JoinedStr value' )
0 commit comments