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