Skip to content

Commit a60ddd3

Browse files
authored
gh-98401: Invalid escape sequences emits SyntaxWarning (#99011)
A backslash-character pair that is not a valid escape sequence now generates a SyntaxWarning, instead of DeprecationWarning. For example, re.compile("\d+\.\d+") now emits a SyntaxWarning ("\d" is an invalid escape sequence), use raw strings for regular expression: re.compile(r"\d+\.\d+"). In a future Python version, SyntaxError will eventually be raised, instead of SyntaxWarning. Octal escapes with value larger than 0o377 (ex: "\477"), deprecated in Python 3.11, now produce a SyntaxWarning, instead of DeprecationWarning. In a future Python version they will be eventually a SyntaxError. codecs.escape_decode() and codecs.unicode_escape_decode() are left unchanged: they still emit DeprecationWarning. * The parser only emits SyntaxWarning for Python 3.12 (feature version), and still emits DeprecationWarning on older Python versions. * Fix SyntaxWarning by using raw strings in Tools/c-analyzer/ and wasm_build.py.
1 parent 916af11 commit a60ddd3

File tree

11 files changed

+69
-29
lines changed

11 files changed

+69
-29
lines changed

Doc/library/re.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ a literal backslash, one might have to write ``'\\\\'`` as the pattern
2929
string, because the regular expression must be ``\\``, and each
3030
backslash must be expressed as ``\\`` inside a regular Python string
3131
literal. Also, please note that any invalid escape sequences in Python's
32-
usage of the backslash in string literals now generate a :exc:`DeprecationWarning`
32+
usage of the backslash in string literals now generate a :exc:`SyntaxWarning`
3333
and in the future this will become a :exc:`SyntaxError`. This behaviour
3434
will happen even if it is a valid escape sequence for a regular expression.
3535

Doc/reference/lexical_analysis.rst

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -612,9 +612,13 @@ Notes:
612612
As in Standard C, up to three octal digits are accepted.
613613

614614
.. versionchanged:: 3.11
615-
Octal escapes with value larger than ``0o377`` produce a :exc:`DeprecationWarning`.
616-
In a future Python version they will be a :exc:`SyntaxWarning` and
617-
eventually a :exc:`SyntaxError`.
615+
Octal escapes with value larger than ``0o377`` produce a
616+
:exc:`DeprecationWarning`.
617+
618+
.. versionchanged:: 3.12
619+
Octal escapes with value larger than ``0o377`` produce a
620+
:exc:`SyntaxWarning`. In a future Python version they will be eventually
621+
a :exc:`SyntaxError`.
618622

619623
(3)
620624
Unlike in Standard C, exactly two hex digits are required.
@@ -646,9 +650,11 @@ escape sequences only recognized in string literals fall into the category of
646650
unrecognized escapes for bytes literals.
647651

648652
.. versionchanged:: 3.6
649-
Unrecognized escape sequences produce a :exc:`DeprecationWarning`. In
650-
a future Python version they will be a :exc:`SyntaxWarning` and
651-
eventually a :exc:`SyntaxError`.
653+
Unrecognized escape sequences produce a :exc:`DeprecationWarning`.
654+
655+
.. versionchanged:: 3.12
656+
Unrecognized escape sequences produce a :exc:`SyntaxWarning`. In a future
657+
Python version they will be eventually a :exc:`SyntaxError`.
652658

653659
Even in a raw literal, quotes can be escaped with a backslash, but the
654660
backslash remains in the result; for example, ``r"\""`` is a valid string

Doc/whatsnew/3.12.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,22 @@ Other Language Changes
121121
chance to execute the GC periodically. (Contributed by Pablo Galindo in
122122
:gh:`97922`.)
123123

124+
* A backslash-character pair that is not a valid escape sequence now generates
125+
a :exc:`SyntaxWarning`, instead of :exc:`DeprecationWarning`.
126+
For example, ``re.compile("\d+\.\d+")`` now emits a :exc:`SyntaxWarning`
127+
(``"\d"`` is an invalid escape sequence), use raw strings for regular
128+
expression: ``re.compile(r"\d+\.\d+")``.
129+
In a future Python version, :exc:`SyntaxError` will eventually be raised,
130+
instead of :exc:`SyntaxWarning`.
131+
(Contributed by Victor Stinner in :gh:`98401`.)
132+
133+
* Octal escapes with value larger than ``0o377`` (ex: ``"\477"``), deprecated
134+
in Python 3.11, now produce a :exc:`SyntaxWarning`, instead of
135+
:exc:`DeprecationWarning`.
136+
In a future Python version they will be eventually a :exc:`SyntaxError`.
137+
(Contributed by Victor Stinner in :gh:`98401`.)
138+
139+
124140
New Modules
125141
===========
126142

Lib/test/test_codeop.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@ def test_filename(self):
310310
def test_warning(self):
311311
# Test that the warning is only returned once.
312312
with warnings_helper.check_warnings(
313-
(".*literal", SyntaxWarning),
314-
(".*invalid", DeprecationWarning),
313+
('"is" with a literal', SyntaxWarning),
314+
("invalid escape sequence", SyntaxWarning),
315315
) as w:
316316
compile_command(r"'\e' is 0")
317317
self.assertEqual(len(w.warnings), 2)
@@ -321,9 +321,9 @@ def test_warning(self):
321321
warnings.simplefilter('error', SyntaxWarning)
322322
compile_command('1 is 1', symbol='exec')
323323

324-
# Check DeprecationWarning treated as an SyntaxError
324+
# Check SyntaxWarning treated as an SyntaxError
325325
with warnings.catch_warnings(), self.assertRaises(SyntaxError):
326-
warnings.simplefilter('error', DeprecationWarning)
326+
warnings.simplefilter('error', SyntaxWarning)
327327
compile_command(r"'\e'", symbol='exec')
328328

329329
def test_incomplete_warning(self):
@@ -337,7 +337,7 @@ def test_invalid_warning(self):
337337
warnings.simplefilter('always')
338338
self.assertInvalid("'\\e' 1")
339339
self.assertEqual(len(w), 1)
340-
self.assertEqual(w[0].category, DeprecationWarning)
340+
self.assertEqual(w[0].category, SyntaxWarning)
341341
self.assertRegex(str(w[0].message), 'invalid escape sequence')
342342
self.assertEqual(w[0].filename, '<input>')
343343

Lib/test/test_fstring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ def test_backslashes_in_string_part(self):
776776
self.assertEqual(f'2\x203', '2 3')
777777
self.assertEqual(f'\x203', ' 3')
778778

779-
with self.assertWarns(DeprecationWarning): # invalid escape sequence
779+
with self.assertWarns(SyntaxWarning): # invalid escape sequence
780780
value = eval(r"f'\{6*7}'")
781781
self.assertEqual(value, '\\42')
782782
self.assertEqual(f'\\{6*7}', '\\42')

Lib/test/test_string_literals.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,19 +109,19 @@ def test_eval_str_invalid_escape(self):
109109
for b in range(1, 128):
110110
if b in b"""\n\r"'01234567NU\\abfnrtuvx""":
111111
continue
112-
with self.assertWarns(DeprecationWarning):
112+
with self.assertWarns(SyntaxWarning):
113113
self.assertEqual(eval(r"'\%c'" % b), '\\' + chr(b))
114114

115115
with warnings.catch_warnings(record=True) as w:
116-
warnings.simplefilter('always', category=DeprecationWarning)
116+
warnings.simplefilter('always', category=SyntaxWarning)
117117
eval("'''\n\\z'''")
118118
self.assertEqual(len(w), 1)
119119
self.assertEqual(str(w[0].message), r"invalid escape sequence '\z'")
120120
self.assertEqual(w[0].filename, '<string>')
121121
self.assertEqual(w[0].lineno, 1)
122122

123123
with warnings.catch_warnings(record=True) as w:
124-
warnings.simplefilter('error', category=DeprecationWarning)
124+
warnings.simplefilter('error', category=SyntaxWarning)
125125
with self.assertRaises(SyntaxError) as cm:
126126
eval("'''\n\\z'''")
127127
exc = cm.exception
@@ -133,11 +133,11 @@ def test_eval_str_invalid_escape(self):
133133

134134
def test_eval_str_invalid_octal_escape(self):
135135
for i in range(0o400, 0o1000):
136-
with self.assertWarns(DeprecationWarning):
136+
with self.assertWarns(SyntaxWarning):
137137
self.assertEqual(eval(r"'\%o'" % i), chr(i))
138138

139139
with warnings.catch_warnings(record=True) as w:
140-
warnings.simplefilter('always', category=DeprecationWarning)
140+
warnings.simplefilter('always', category=SyntaxWarning)
141141
eval("'''\n\\407'''")
142142
self.assertEqual(len(w), 1)
143143
self.assertEqual(str(w[0].message),
@@ -146,7 +146,7 @@ def test_eval_str_invalid_octal_escape(self):
146146
self.assertEqual(w[0].lineno, 1)
147147

148148
with warnings.catch_warnings(record=True) as w:
149-
warnings.simplefilter('error', category=DeprecationWarning)
149+
warnings.simplefilter('error', category=SyntaxWarning)
150150
with self.assertRaises(SyntaxError) as cm:
151151
eval("'''\n\\407'''")
152152
exc = cm.exception
@@ -186,19 +186,19 @@ def test_eval_bytes_invalid_escape(self):
186186
for b in range(1, 128):
187187
if b in b"""\n\r"'01234567\\abfnrtvx""":
188188
continue
189-
with self.assertWarns(DeprecationWarning):
189+
with self.assertWarns(SyntaxWarning):
190190
self.assertEqual(eval(r"b'\%c'" % b), b'\\' + bytes([b]))
191191

192192
with warnings.catch_warnings(record=True) as w:
193-
warnings.simplefilter('always', category=DeprecationWarning)
193+
warnings.simplefilter('always', category=SyntaxWarning)
194194
eval("b'''\n\\z'''")
195195
self.assertEqual(len(w), 1)
196196
self.assertEqual(str(w[0].message), r"invalid escape sequence '\z'")
197197
self.assertEqual(w[0].filename, '<string>')
198198
self.assertEqual(w[0].lineno, 1)
199199

200200
with warnings.catch_warnings(record=True) as w:
201-
warnings.simplefilter('error', category=DeprecationWarning)
201+
warnings.simplefilter('error', category=SyntaxWarning)
202202
with self.assertRaises(SyntaxError) as cm:
203203
eval("b'''\n\\z'''")
204204
exc = cm.exception
@@ -209,11 +209,11 @@ def test_eval_bytes_invalid_escape(self):
209209

210210
def test_eval_bytes_invalid_octal_escape(self):
211211
for i in range(0o400, 0o1000):
212-
with self.assertWarns(DeprecationWarning):
212+
with self.assertWarns(SyntaxWarning):
213213
self.assertEqual(eval(r"b'\%o'" % i), bytes([i & 0o377]))
214214

215215
with warnings.catch_warnings(record=True) as w:
216-
warnings.simplefilter('always', category=DeprecationWarning)
216+
warnings.simplefilter('always', category=SyntaxWarning)
217217
eval("b'''\n\\407'''")
218218
self.assertEqual(len(w), 1)
219219
self.assertEqual(str(w[0].message),
@@ -222,7 +222,7 @@ def test_eval_bytes_invalid_octal_escape(self):
222222
self.assertEqual(w[0].lineno, 1)
223223

224224
with warnings.catch_warnings(record=True) as w:
225-
warnings.simplefilter('error', category=DeprecationWarning)
225+
warnings.simplefilter('error', category=SyntaxWarning)
226226
with self.assertRaises(SyntaxError) as cm:
227227
eval("b'''\n\\407'''")
228228
exc = cm.exception
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
A backslash-character pair that is not a valid escape sequence now generates a
2+
:exc:`SyntaxWarning`, instead of :exc:`DeprecationWarning`. For example,
3+
``re.compile("\d+\.\d+")`` now emits a :exc:`SyntaxWarning` (``"\d"`` is an
4+
invalid escape sequence), use raw strings for regular expression:
5+
``re.compile(r"\d+\.\d+")``. In a future Python version, :exc:`SyntaxError`
6+
will eventually be raised, instead of :exc:`SyntaxWarning`. Patch by Victor
7+
Stinner.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Octal escapes with value larger than ``0o377`` (ex: ``"\477"``), deprecated
2+
in Python 3.11, now produce a :exc:`SyntaxWarning`, instead of
3+
:exc:`DeprecationWarning`. In a future Python version they will be
4+
eventually a :exc:`SyntaxError`. Patch by Victor Stinner.

Parser/string_parser.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,16 @@ warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token
2121
if (msg == NULL) {
2222
return -1;
2323
}
24-
if (PyErr_WarnExplicitObject(PyExc_DeprecationWarning, msg, p->tok->filename,
24+
PyObject *category;
25+
if (p->feature_version >= 12) {
26+
category = PyExc_SyntaxWarning;
27+
}
28+
else {
29+
category = PyExc_DeprecationWarning;
30+
}
31+
if (PyErr_WarnExplicitObject(category, msg, p->tok->filename,
2532
t->lineno, NULL, NULL) < 0) {
26-
if (PyErr_ExceptionMatches(PyExc_DeprecationWarning)) {
33+
if (PyErr_ExceptionMatches(category)) {
2734
/* Replace the DeprecationWarning exception with a SyntaxError
2835
to get a more accurate error report */
2936
PyErr_Clear();

Tools/c-analyzer/c_parser/_state_machine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def parse(srclines):
9696
# # end matched parens
9797
# ''')
9898

99-
'''
99+
r'''
100100
# for loop
101101
(?:
102102
\s* \b for

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy