gh-115154: Fix untokenize handling of unicode named literals (#115171) · python/cpython@ecf16ee

GitHub

@@ -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):