mirror of
https://github.com/python/cpython.git
synced 2025-11-07 09:02:02 +00:00
gh-137314: Fix incorrect treatment of format specs in raw fstrings (#137328)
This commit is contained in:
parent
676748d4da
commit
0153d82a5a
3 changed files with 42 additions and 1 deletions
|
|
@ -1831,6 +1831,34 @@ def test_newlines_in_format_specifiers(self):
|
||||||
for case in valid_cases:
|
for case in valid_cases:
|
||||||
compile(case, "<string>", "exec")
|
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 escape sequences
|
||||||
|
self.assertEqual(f"{UnchangedFormat():\xFF}", 'ÿ')
|
||||||
|
self.assertEqual(rf"{UnchangedFormat():\xFF}", '\\xFF')
|
||||||
|
|
||||||
|
# Test nested expressions with raw/non-raw combinations
|
||||||
|
self.assertEqual(rf"{UnchangedFormat():{'\xFF'}}", 'ÿ')
|
||||||
|
self.assertEqual(f"{UnchangedFormat():{r'\xFF'}}", '\\xFF')
|
||||||
|
self.assertEqual(rf"{UnchangedFormat():{r'\xFF'}}", '\\xFF')
|
||||||
|
|
||||||
|
# Test continuation character in format specs
|
||||||
|
self.assertEqual(f"""{UnchangedFormat():{'a'\
|
||||||
|
'b'}}""", 'ab')
|
||||||
|
self.assertEqual(rf"""{UnchangedFormat():{'a'\
|
||||||
|
'b'}}""", 'ab')
|
||||||
|
|
||||||
|
# Test multiple format specs in same raw f-string
|
||||||
|
self.assertEqual(rf"{UnchangedFormat():\xFF} {UnchangedFormat():\n}", '\\xFF \\n')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -1404,7 +1404,15 @@ expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok) {
|
||||||
if (PyBytes_AsStringAndSize(tok->bytes, &bstr, &bsize) == -1) {
|
if (PyBytes_AsStringAndSize(tok->bytes, &bstr, &bsize) == -1) {
|
||||||
return NULL;
|
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) {
|
if (str == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue