Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
35 changes: 35 additions & 0 deletions Lib/test/test_fstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -1831,6 +1831,41 @@ def test_newlines_in_format_specifiers(self):
for case in valid_cases:
compile(case, "<string>", "exec")

def test_raw_fstring_format_spec(self):
# Test raw f-string format spec behavior (Issue #137314).
#
# Raw f-strings should preserve literal backslashes in format specifications,
# not interpret them as escape sequences.
class UnchangedFormat:
"""Test helper that returns the format spec unchanged."""
def __format__(self, format):
return format

# Test basic Unicode escape
non_raw = f"{UnchangedFormat():\xFF}"
self.assertEqual(non_raw, 'ÿ')

raw = rf"{UnchangedFormat():\xFF}"
self.assertEqual(raw, '\\xFF')

# Test newline escape
non_raw = f"{UnchangedFormat():\n}"
self.assertEqual(non_raw, '\n')

raw = rf"{UnchangedFormat():\n}"
self.assertEqual(raw, '\\n')

# Test tab escape
non_raw = f"{UnchangedFormat():\t}"
self.assertEqual(non_raw, '\t')

raw = rf"{UnchangedFormat():\t}"
self.assertEqual(raw, '\\t')

# Test multiple format specs in same raw f-string
result = rf"{UnchangedFormat():\xFF} {UnchangedFormat():\n}"
self.assertEqual(result, '\\xFF \\n')


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fixed a regression where raw f-strings incorrectly interpreted
escape sequences in format specifications. Raw f-strings now properly preserve
literal backslashes in format specs, matching the behavior from Python 3.11.
For example, ``rf"{obj:\xFF}"`` now correctly produces ``'\\xFF'`` instead of
``'ÿ'``. Patch by Pablo Galindo.
10 changes: 9 additions & 1 deletion Parser/action_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,15 @@ expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok) {
if (PyBytes_AsStringAndSize(tok->bytes, &bstr, &bsize) == -1) {
return NULL;
}
PyObject* str = _PyPegen_decode_string(p, 0, bstr, bsize, tok);

// Check if we're inside a raw f-string for format spec decoding
int is_raw = 0;
if (INSIDE_FSTRING(p->tok)) {
tokenizer_mode *mode = TOK_GET_MODE(p->tok);
is_raw = mode->raw;
}

PyObject* str = _PyPegen_decode_string(p, is_raw, bstr, bsize, tok);
if (str == NULL) {
return NULL;
}
Expand Down
Loading