Skip to content

Commit b15aa04

Browse files
committed
gh-13731: Fix incorrect treatment of format specs in raw fstrings
1 parent 801cf3f commit b15aa04

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

Lib/test/test_fstring.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,6 +1831,42 @@ def test_newlines_in_format_specifiers(self):
18311831
for case in valid_cases:
18321832
compile(case, "<string>", "exec")
18331833

1834+
def test_raw_fstring_format_spec(self):
1835+
"""Test raw f-string format spec behavior (Issue #137314).
1836+
1837+
Raw f-strings should preserve literal backslashes in format specifications,
1838+
not interpret them as escape sequences.
1839+
"""
1840+
class UnchangedFormat:
1841+
"""Test helper that returns the format spec unchanged."""
1842+
def __format__(self, format):
1843+
return format
1844+
1845+
# Test basic Unicode escape
1846+
non_raw = f"{UnchangedFormat():\xFF}"
1847+
self.assertEqual(non_raw, 'ÿ')
1848+
1849+
raw = rf"{UnchangedFormat():\xFF}"
1850+
self.assertEqual(raw, '\\xFF')
1851+
1852+
# Test newline escape
1853+
non_raw = f"{UnchangedFormat():\n}"
1854+
self.assertEqual(non_raw, '\n')
1855+
1856+
raw = rf"{UnchangedFormat():\n}"
1857+
self.assertEqual(raw, '\\n')
1858+
1859+
# Test tab escape
1860+
non_raw = f"{UnchangedFormat():\t}"
1861+
self.assertEqual(non_raw, '\t')
1862+
1863+
raw = rf"{UnchangedFormat():\t}"
1864+
self.assertEqual(raw, '\\t')
1865+
1866+
# Test multiple format specs in same raw f-string
1867+
result = rf"{UnchangedFormat():\xFF} {UnchangedFormat():\n}"
1868+
self.assertEqual(result, '\\xFF \\n')
1869+
18341870

18351871
if __name__ == '__main__':
18361872
unittest.main()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixed a regression where raw f-strings incorrectly interpreted
2+
escape sequences in format specifications. Raw f-strings now properly preserve
3+
literal backslashes in format specs, matching the behavior from Python 3.11.
4+
For example, ``rf"{obj:\xFF}"`` now correctly produces ``'\\xFF'`` instead of
5+
``'ÿ'``. Patch by Pablo Galindo.

Parser/action_helpers.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,15 @@ expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok) {
14041404
if (PyBytes_AsStringAndSize(tok->bytes, &bstr, &bsize) == -1) {
14051405
return NULL;
14061406
}
1407-
PyObject* str = _PyPegen_decode_string(p, 0, bstr, bsize, tok);
1407+
1408+
// Check if we're inside a raw f-string for format spec decoding
1409+
int is_raw = 0;
1410+
if (INSIDE_FSTRING(p->tok)) {
1411+
tokenizer_mode *mode = TOK_GET_MODE(p->tok);
1412+
is_raw = mode->raw;
1413+
}
1414+
1415+
PyObject* str = _PyPegen_decode_string(p, is_raw, bstr, bsize, tok);
14081416
if (str == NULL) {
14091417
return NULL;
14101418
}

0 commit comments

Comments
 (0)