Skip to content

Commit 4192ead

Browse files
[3.13] gh-154711: Strip comments after escaped quotes in f-string debug text (#158310)
When a replacement field ended with a string that had an escaped quote and was followed by a comment, the comment leaked into the debug f-string output. The comment detection loop in set_fstring_expr skips escaped characters, but the comment stripping loop did not. So an escaped quote left the scanner thinking it was still inside a string, and the comment was never removed. This makes the stripping loop skip escaped characters the same way the detection loop does. 3.14 and later already have this fix from gh-154719.
1 parent 036d2d6 commit 4192ead

3 files changed

Lines changed: 21 additions & 0 deletions

File tree

‎Lib/test/test_fstring.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,11 @@ def __repr__(self):
16641664
self.assertEqual(f'{" # nooo "=}', '" # nooo "=\' # nooo \'')
16651665
self.assertEqual(f'{" \" # nooo \" "=}', '" \\" # nooo \\" "=\' " # nooo " \'')
16661666

1667+
# A real comment after a string that ends with an escaped quote must
1668+
# still be stripped (gh-154711).
1669+
self.assertEqual(f"{'\'' = # comment
1670+
}", "'\\'' = \n" + repr("'"))
1671+
16671672
self.assertEqual(f'{ # some comment goes here
16681673
"""hello"""=}', ' \n """hello"""=\'hello\'')
16691674
self.assertEqual(f'{"""# this is not a comment
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a bug where a comment placed after a string that ends with an escaped
2+
quote could leak into the output of a debug f-string. Now the comment is
3+
stripped as expected.

‎Parser/lexer/lexer.c‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ set_fstring_expr(struct tok_state* tok, struct token *token, char c) {
162162
while (i < expression_size) {
163163
char ch = expression[i];
164164

165+
// Copy escaped characters as-is. This keeps an escaped quote from
166+
// flipping the in_string state, which would otherwise stop a real
167+
// comment from being detected (see the detection loop above).
168+
if (ch == '\\') {
169+
result[j++] = ch;
170+
i++;
171+
if (i < expression_size) {
172+
result[j++] = expression[i];
173+
i++;
174+
}
175+
continue;
176+
}
177+
165178
// Handle string quotes
166179
if (ch == '"' || ch == '\'') {
167180
// See comment above to understand this part

0 commit comments

Comments
 (0)