mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
[3.12] gh-105938: Emit a SyntaxWarning for escaped braces in an f-string (GH-105939) (#105941)
(cherry picked from commit 6586cee27f
)
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
This commit is contained in:
parent
cc18a8b78a
commit
fea0d2fbaa
3 changed files with 18 additions and 6 deletions
|
@ -13,6 +13,7 @@ import re
|
||||||
import types
|
import types
|
||||||
import decimal
|
import decimal
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
from test import support
|
from test import support
|
||||||
from test.support.os_helper import temp_cwd
|
from test.support.os_helper import temp_cwd
|
||||||
from test.support.script_helper import assert_python_failure, assert_python_ok
|
from test.support.script_helper import assert_python_failure, assert_python_ok
|
||||||
|
@ -904,7 +905,7 @@ x = (
|
||||||
self.assertEqual(f'2\x203', '2 3')
|
self.assertEqual(f'2\x203', '2 3')
|
||||||
self.assertEqual(f'\x203', ' 3')
|
self.assertEqual(f'\x203', ' 3')
|
||||||
|
|
||||||
with self.assertWarns(DeprecationWarning): # invalid escape sequence
|
with self.assertWarns(SyntaxWarning): # invalid escape sequence
|
||||||
value = eval(r"f'\{6*7}'")
|
value = eval(r"f'\{6*7}'")
|
||||||
self.assertEqual(value, '\\42')
|
self.assertEqual(value, '\\42')
|
||||||
with self.assertWarns(SyntaxWarning): # invalid escape sequence
|
with self.assertWarns(SyntaxWarning): # invalid escape sequence
|
||||||
|
@ -1037,7 +1038,7 @@ x = (
|
||||||
]
|
]
|
||||||
for case, expected_result in deprecated_cases:
|
for case, expected_result in deprecated_cases:
|
||||||
with self.subTest(case=case, expected_result=expected_result):
|
with self.subTest(case=case, expected_result=expected_result):
|
||||||
with self.assertWarns(DeprecationWarning):
|
with self.assertWarns(SyntaxWarning):
|
||||||
result = eval(case)
|
result = eval(case)
|
||||||
self.assertEqual(result, expected_result)
|
self.assertEqual(result, expected_result)
|
||||||
self.assertEqual(fr'\{{\}}', '\\{\\}')
|
self.assertEqual(fr'\{{\}}', '\\{\\}')
|
||||||
|
@ -1046,6 +1047,12 @@ x = (
|
||||||
self.assertEqual(fr'\}}{1+1}', '\\}2')
|
self.assertEqual(fr'\}}{1+1}', '\\}2')
|
||||||
self.assertEqual(fr'{1+1}\}}', '2\\}')
|
self.assertEqual(fr'{1+1}\}}', '2\\}')
|
||||||
|
|
||||||
|
def test_fstring_backslash_before_double_bracket_warns_once(self):
|
||||||
|
with warnings.catch_warnings(record=True) as w:
|
||||||
|
eval(r"f'\{{'")
|
||||||
|
self.assertEqual(len(w), 1)
|
||||||
|
self.assertEqual(w[0].category, SyntaxWarning)
|
||||||
|
|
||||||
def test_fstring_backslash_prefix_raw(self):
|
def test_fstring_backslash_prefix_raw(self):
|
||||||
self.assertEqual(f'\\', '\\')
|
self.assertEqual(f'\\', '\\')
|
||||||
self.assertEqual(f'\\\\', '\\\\')
|
self.assertEqual(f'\\\\', '\\\\')
|
||||||
|
|
|
@ -12,6 +12,11 @@ static int
|
||||||
warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token *t)
|
warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token *t)
|
||||||
{
|
{
|
||||||
unsigned char c = *first_invalid_escape;
|
unsigned char c = *first_invalid_escape;
|
||||||
|
if ((t->type == FSTRING_MIDDLE || t->type == FSTRING_END) && (c == '{' || c == '}')) { // in this case the tokenizer has already emitted a warning,
|
||||||
|
// see tokenizer.c:warn_invalid_escape_sequence
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int octal = ('4' <= c && c <= '7');
|
int octal = ('4' <= c && c <= '7');
|
||||||
PyObject *msg =
|
PyObject *msg =
|
||||||
octal
|
octal
|
||||||
|
@ -31,7 +36,7 @@ warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token
|
||||||
if (PyErr_WarnExplicitObject(category, msg, p->tok->filename,
|
if (PyErr_WarnExplicitObject(category, msg, p->tok->filename,
|
||||||
t->lineno, NULL, NULL) < 0) {
|
t->lineno, NULL, NULL) < 0) {
|
||||||
if (PyErr_ExceptionMatches(category)) {
|
if (PyErr_ExceptionMatches(category)) {
|
||||||
/* Replace the DeprecationWarning exception with a SyntaxError
|
/* Replace the Syntax/DeprecationWarning exception with a SyntaxError
|
||||||
to get a more accurate error report */
|
to get a more accurate error report */
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
|
|
||||||
|
|
|
@ -1559,12 +1559,12 @@ warn_invalid_escape_sequence(struct tok_state *tok, int first_invalid_escape_cha
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyErr_WarnExplicitObject(PyExc_DeprecationWarning, msg, tok->filename,
|
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, tok->filename,
|
||||||
tok->lineno, NULL, NULL) < 0) {
|
tok->lineno, NULL, NULL) < 0) {
|
||||||
Py_DECREF(msg);
|
Py_DECREF(msg);
|
||||||
|
|
||||||
if (PyErr_ExceptionMatches(PyExc_DeprecationWarning)) {
|
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
|
||||||
/* Replace the DeprecationWarning exception with a SyntaxError
|
/* Replace the SyntaxWarning exception with a SyntaxError
|
||||||
to get a more accurate error report */
|
to get a more accurate error report */
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
return syntaxerror(tok, "invalid escape sequence '\\%c'", (char) first_invalid_escape_char);
|
return syntaxerror(tok, "invalid escape sequence '\\%c'", (char) first_invalid_escape_char);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue