@@ -168,6 +168,7 @@ def __init__(self):
168
168
self .tokens = []
169
169
self .prev_row = 1
170
170
self .prev_col = 0
171
+ self .prev_type = None
171
172
self .encoding = None
172
173
173
174
def add_whitespace (self , start ):
@@ -183,6 +184,29 @@ def add_whitespace(self, start):
183
184
if col_offset :
184
185
self .tokens .append (" " * col_offset )
185
186
187
+ def escape_brackets (self , token ):
188
+ characters = []
189
+ consume_until_next_bracket = False
190
+ for character in token :
191
+ if character == "}" :
192
+ if consume_until_next_bracket :
193
+ consume_until_next_bracket = False
194
+ else :
195
+ characters .append (character )
196
+ if character == "{" :
197
+ n_backslashes = sum (
198
+ 1 for char in _itertools .takewhile (
199
+ "\\ " .__eq__ ,
200
+ characters [- 2 ::- 1 ]
201
+ )
202
+ )
203
+ if n_backslashes % 2 == 0 :
204
+ characters .append (character )
205
+ else :
206
+ consume_until_next_bracket = True
207
+ characters .append (character )
208
+ return "" .join (characters )
209
+
186
210
def untokenize (self , iterable ):
187
211
it = iter (iterable )
188
212
indents = []
@@ -214,25 +238,29 @@ def untokenize(self, iterable):
214
238
startline = False
215
239
elif tok_type == FSTRING_MIDDLE :
216
240
if '{' in token or '}' in token :
241
+ token = self .escape_brackets (token )
242
+ last_line = token .splitlines ()[- 1 ]
217
243
end_line , end_col = end
218
- end = ( end_line , end_col + token .count ('{' ) + token .count ('}' ) )
219
- token = re . sub ( '{' , '{{' , token )
220
- token = re . sub ( '}' , '}}' , token )
221
-
244
+ extra_chars = last_line .count ("{{" ) + last_line .count ("}}" )
245
+ end = ( end_line , end_col + extra_chars )
246
+ elif tok_type in ( STRING , FSTRING_START ) and self . prev_type in ( STRING , FSTRING_END ):
247
+ self . tokens . append ( " " )
222
248
223
249
self .add_whitespace (start )
224
250
self .tokens .append (token )
225
251
self .prev_row , self .prev_col = end
226
252
if tok_type in (NEWLINE , NL ):
227
253
self .prev_row += 1
228
254
self .prev_col = 0
255
+ self .prev_type = tok_type
229
256
return "" .join (self .tokens )
230
257
231
258
def compat (self , token , iterable ):
232
259
indents = []
233
260
toks_append = self .tokens .append
234
261
startline = token [0 ] in (NEWLINE , NL )
235
262
prevstring = False
263
+ in_fstring = 0
236
264
237
265
for tok in _itertools .chain ([token ], iterable ):
238
266
toknum , tokval = tok [:2 ]
@@ -251,6 +279,10 @@ def compat(self, token, iterable):
251
279
else :
252
280
prevstring = False
253
281
282
+ if toknum == FSTRING_START :
283
+ in_fstring += 1
284
+ elif toknum == FSTRING_END :
285
+ in_fstring -= 1
254
286
if toknum == INDENT :
255
287
indents .append (tokval )
256
288
continue
@@ -263,11 +295,18 @@ def compat(self, token, iterable):
263
295
toks_append (indents [- 1 ])
264
296
startline = False
265
297
elif toknum == FSTRING_MIDDLE :
266
- if '{' in tokval or '}' in tokval :
267
- tokval = re .sub ('{' , '{{' , tokval )
268
- tokval = re .sub ('}' , '}}' , tokval )
298
+ tokval = self .escape_brackets (tokval )
299
+
300
+ # Insert a space between two consecutive brackets if we are in an f-string
301
+ if tokval in {"{" , "}" } and self .tokens and self .tokens [- 1 ] == tokval and in_fstring :
302
+ tokval = ' ' + tokval
303
+
304
+ # Insert a space between two consecutive f-strings
305
+ if toknum in (STRING , FSTRING_START ) and self .prev_type in (STRING , FSTRING_END ):
306
+ self .tokens .append (" " )
269
307
270
308
toks_append (tokval )
309
+ self .prev_type = toknum
271
310
272
311
273
312
def untokenize (iterable ):
0 commit comments