You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note the absence of a second { in the {{ after the \n — but in no other positions.
Unlike some other roundtrip failures of tokenize, some of which are minor infelicities, this one actually creates a syntactically invalid program on roundtrip, which is quite bad. You get a SyntaxError: f-string: single '}' is not allowed when trying to use the results.
It looks like this was a regression in Python 3.12; I can't reproduce the behaviour with Python 3.11. I'm guessing it was caused by the PEP-701 changes.
This PR fixed the handling of Unicode literals (e.g. \\N{foo}), but it seems to only
be checking for the presence of backslashes without checking if they are followed by N. This appears to fix that:
if character == "{":
n_backslashes = sum(
1 for char in _itertools.takewhile(
"\\".__eq__,
characters[-2::-1]
)
)
- if n_backslashes % 2 == 0:+ if n_backslashes % 2 == 0 or characters[-1] != "N":
characters.append(character)
else:
consume_until_next_bracket = True
Extremely late for me to say this, but I thought I'd add: I only unearthed this bug because I'm working on code that incidentally tries to tokenizer-roundtrip everything in https://github.com/hauntsaninja/mypy_primer. So, doing something like that (possibly literally just that) as a test case for tokenize could perhaps be a good idea to prevent future regressions — although setting that up sounds like a hassle!
Activity
wyattscarpenter commentedon Oct 5, 2024
Furthermore, here is the output of the following code:
So, it seems that the line is getting in alright, but the \n{{ is getting turned into a \n{ in the tokenizer somehow.
Same erroneous output for the bytes version (with rb-string, BytesIO and tokenize.tokenize).
AlexWaygood commentedon Oct 5, 2024
It looks like this was a regression in Python 3.12; I can't reproduce the behaviour with Python 3.11. I'm guessing it was caused by the PEP-701 changes.
AlexWaygood commentedon Oct 5, 2024
Reproduced on the
mainbranch as well.tomasr8 commentedon Oct 5, 2024
This seems to happen with other escape characters as well:
tomasr8 commentedon Oct 5, 2024
I think the issue is in this method:
cpython/Lib/tokenize.py
Lines 187 to 208 in 16cd6cc
This PR fixed the handling of Unicode literals (e.g.
\\N{foo}), but it seems to onlybe checking for the presence of backslashes without checking if they are followed by
N. This appears to fix that:if character == "{": n_backslashes = sum( 1 for char in _itertools.takewhile( "\\".__eq__, characters[-2::-1] ) ) - if n_backslashes % 2 == 0: + if n_backslashes % 2 == 0 or characters[-1] != "N": characters.append(character) else: consume_until_next_bracket = Truegh-125008: Fix `tokenize.untokenize` roundtrip for `\n{{` (#125013)
pythongh-125008: Fix `tokenize.untokenize` roundtrip for `\n{{` (pyth…
pythongh-125008: Fix `tokenize.untokenize` roundtrip for `\n{{` (pyth…
[3.12] gh-125008: Fix `tokenize.untokenize` roundtrip for `\n{{` (GH-…
[3.13] gh-125008: Fix `tokenize.untokenize` roundtrip for `\n{{` (GH-…
implement workaround for \n{{ roundtripping bug
wyattscarpenter commentedon Oct 26, 2024
Extremely late for me to say this, but I thought I'd add: I only unearthed this bug because I'm working on code that incidentally tries to tokenizer-roundtrip everything in https://github.com/hauntsaninja/mypy_primer. So, doing something like that (possibly literally just that) as a test case for tokenize could perhaps be a good idea to prevent future regressions — although setting that up sounds like a hassle!
tomasr8 commentedon Oct 26, 2024
We actually already do that here for some random files in the test folder:
cpython/Lib/test/test_tokenize.py
Lines 1822 to 1835 in 26d6277
Though, so far it only compares the tokens, not the actual source code. I'd like to extend this check to compare the source code as well here: #126010