@@ -168,6 +168,7 @@ def __init__(self):
168168self.tokens= []
169169self.prev_row=1
170170self.prev_col=0
171+self.prev_type=None
171172self.encoding=None
172173173174defadd_whitespace(self, start):
@@ -183,6 +184,29 @@ def add_whitespace(self, start):
183184ifcol_offset:
184185self.tokens.append(" "*col_offset)
185186187+defescape_brackets(self, token):
188+characters= []
189+consume_until_next_bracket=False
190+forcharacterintoken:
191+ifcharacter=="}":
192+ifconsume_until_next_bracket:
193+consume_until_next_bracket=False
194+else:
195+characters.append(character)
196+ifcharacter=="{":
197+n_backslashes=sum(
198+1forcharin_itertools.takewhile(
199+"\\".__eq__,
200+characters[-2::-1]
201+ )
202+ )
203+ifn_backslashes%2==0:
204+characters.append(character)
205+else:
206+consume_until_next_bracket=True
207+characters.append(character)
208+return"".join(characters)
209+186210defuntokenize(self, iterable):
187211it=iter(iterable)
188212indents= []
@@ -214,25 +238,29 @@ def untokenize(self, iterable):
214238startline=False
215239eliftok_type==FSTRING_MIDDLE:
216240if'{'intokenor'}'intoken:
241+token=self.escape_brackets(token)
242+last_line=token.splitlines()[-1]
217243end_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+eliftok_typein (STRING, FSTRING_START) andself.prev_typein (STRING, FSTRING_END):
247+self.tokens.append(" ")
222248223249self.add_whitespace(start)
224250self.tokens.append(token)
225251self.prev_row, self.prev_col=end
226252iftok_typein (NEWLINE, NL):
227253self.prev_row+=1
228254self.prev_col=0
255+self.prev_type=tok_type
229256return"".join(self.tokens)
230257231258defcompat(self, token, iterable):
232259indents= []
233260toks_append=self.tokens.append
234261startline=token[0] in (NEWLINE, NL)
235262prevstring=False
263+in_fstring=0
236264237265fortokin_itertools.chain([token], iterable):
238266toknum, tokval=tok[:2]
@@ -251,6 +279,10 @@ def compat(self, token, iterable):
251279else:
252280prevstring=False
253281282+iftoknum==FSTRING_START:
283+in_fstring+=1
284+eliftoknum==FSTRING_END:
285+in_fstring-=1
254286iftoknum==INDENT:
255287indents.append(tokval)
256288continue
@@ -263,11 +295,18 @@ def compat(self, token, iterable):
263295toks_append(indents[-1])
264296startline=False
265297eliftoknum==FSTRING_MIDDLE:
266-if'{'intokvalor'}'intokval:
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+iftokvalin {"{", "}"} andself.tokensandself.tokens[-1] ==tokvalandin_fstring:
302+tokval=' '+tokval
303+304+# Insert a space between two consecutive f-strings
305+iftoknumin (STRING, FSTRING_START) andself.prev_typein (STRING, FSTRING_END):
306+self.tokens.append(" ")
269307270308toks_append(tokval)
309+self.prev_type=toknum
271310272311273312defuntokenize(iterable):