Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Lib/test/test_fstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -1678,6 +1678,11 @@ def __repr__(self):
self.assertEqual(f'{" # nooo "=}', '" # nooo "=\' # nooo \'')
self.assertEqual(f'{" \" # nooo \" "=}', '" \\" # nooo \\" "=\' " # nooo " \'')

# A real comment after a string that ends with an escaped quote must
# still be stripped (gh-154711).
self.assertEqual(f"{'\'' = # comment
}", "'\\'' = \n" + repr("'"))

self.assertEqual(f'{ # some comment goes here
"""hello"""=}', ' \n """hello"""=\'hello\'')
self.assertEqual(f'{"""# this is not a comment
Expand Down
12 changes: 12 additions & 0 deletions Lib/test/test_tstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ def test_debug_specifier(self):
)
self.assertEqual(fstring(t), "Value: value = 42")

def test_comment_after_escaped_quote(self):
# A comment after a string that ends with an escaped quote must be
# stripped from the interpolation expression (gh-154711).
t = t"{'a\'b' # comment
}"
self.assertEqual(t.interpolations[0].expression, "'a\\'b'")

t = t"{'a\'b' = # comment
}"
self.assertEqual(t.interpolations[0].expression, "'a\\'b'")
self.assertEqual(t.strings, ("'a\\'b' = \n", ""))

def test_raw_tstrings(self):
path = r"C:\Users"
t = rt"{path}\Documents"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a bug where a comment placed after a string that ends with an escaped
quote could leak into the output of a debug f-string and into t-string
metadata. Now the comment is stripped as expected.
13 changes: 13 additions & 0 deletions Parser/lexer/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ _PyLexer_set_ftstring_expr(struct tok_state* tok, struct token *token, char c) {
while (i < tok_mode->last_expr_size - tok_mode->last_expr_end) {
char ch = tok_mode->last_expr_buffer[i];

// Copy escaped characters as-is. This keeps an escaped quote from
// flipping the in_string state, which would otherwise stop a real
// comment from being detected (see the detection loop above).
if (ch == '\\') {
result[j++] = ch;
i++;
if (i < tok_mode->last_expr_size - tok_mode->last_expr_end) {
result[j++] = tok_mode->last_expr_buffer[i];
i++;
}
continue;
}

// Handle string quotes
if (ch == '"' || ch == '\'') {
// See comment above to understand this part
Expand Down
Loading