GeezerVison is showing github.com — gh-98401: Invalid escape sequences emits SyntaxWarning (#99011) · python/cpython@a60ddd3
File treeExpand file treeCollapse file tree Open diff view settings Misc/NEWS.d/next/Core and Builtins
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Doc/library/re.rst +1-1Lines changed: 1 addition & 1 deletion - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -29,7 +29,7 @@ a literal backslash, one might have to write ``'\\\\'`` as the pattern | 29 | 29 | string, because the regular expression must be ``\\``, and each | 30 | 30 | backslash must be expressed as ``\\`` inside a regular Python string | 31 | 31 | 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` | 33 | 33 | and in the future this will become a :exc:`SyntaxError`. This behaviour | 34 | 34 | will happen even if it is a valid escape sequence for a regular expression. | 35 | 35 |
| |
Collapse file Copy file name to clipboardExpand all lines: Doc/reference/lexical_analysis.rst +12-6Lines changed: 12 additions & 6 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -612,9 +612,13 @@ Notes: | 612 | 612 | As in Standard C, up to three octal digits are accepted. | 613 | 613 |
| 614 | 614 | .. 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`. | 618 | 622 |
| 619 | 623 | (3) | 620 | 624 | 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 | 646 | 650 | unrecognized escapes for bytes literals. | 647 | 651 |
| 648 | 652 | .. 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`. | 652 | 658 |
| 653 | 659 | Even in a raw literal, quotes can be escaped with a backslash, but the | 654 | 660 | backslash remains in the result; for example, ``r"\""`` is a valid string | |
Collapse file Copy file name to clipboardExpand all lines: Doc/whatsnew/3.12.rst +16Lines changed: 16 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -121,6 +121,22 @@ Other Language Changes | 121 | 121 | chance to execute the GC periodically. (Contributed by Pablo Galindo in | 122 | 122 | :gh:`97922`.) | 123 | 123 |
| | 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 | + | 124 | 140 | New Modules | 125 | 141 | =========== | 126 | 142 |
| |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_codeop.py +5-5Lines changed: 5 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -310,8 +310,8 @@ def test_filename(self): | 310 | 310 | def test_warning(self): | 311 | 311 | # Test that the warning is only returned once. | 312 | 312 | with warnings_helper.check_warnings( | 313 | | - (".*literal", SyntaxWarning), | 314 | | - (".*invalid", DeprecationWarning), | | 313 | + ('"is" with a literal', SyntaxWarning), | | 314 | + ("invalid escape sequence", SyntaxWarning), | 315 | 315 | ) as w: | 316 | 316 | compile_command(r"'\e' is 0") | 317 | 317 | self.assertEqual(len(w.warnings), 2) | @@ -321,9 +321,9 @@ def test_warning(self): | 321 | 321 | warnings.simplefilter('error', SyntaxWarning) | 322 | 322 | compile_command('1 is 1', symbol='exec') | 323 | 323 |
| 324 | | - # Check DeprecationWarning treated as an SyntaxError | | 324 | + # Check SyntaxWarning treated as an SyntaxError | 325 | 325 | with warnings.catch_warnings(), self.assertRaises(SyntaxError): | 326 | | - warnings.simplefilter('error', DeprecationWarning) | | 326 | + warnings.simplefilter('error', SyntaxWarning) | 327 | 327 | compile_command(r"'\e'", symbol='exec') | 328 | 328 |
| 329 | 329 | def test_incomplete_warning(self): | @@ -337,7 +337,7 @@ def test_invalid_warning(self): | 337 | 337 | warnings.simplefilter('always') | 338 | 338 | self.assertInvalid("'\\e' 1") | 339 | 339 | self.assertEqual(len(w), 1) | 340 | | - self.assertEqual(w[0].category, DeprecationWarning) | | 340 | + self.assertEqual(w[0].category, SyntaxWarning) | 341 | 341 | self.assertRegex(str(w[0].message), 'invalid escape sequence') | 342 | 342 | self.assertEqual(w[0].filename, '<input>') | 343 | 343 |
| |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_fstring.py +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -776,7 +776,7 @@ def test_backslashes_in_string_part(self): | 776 | 776 | self.assertEqual(f'2\x203', '2 3') | 777 | 777 | self.assertEqual(f'\x203', ' 3') | 778 | 778 |
| 779 | | - with self.assertWarns(DeprecationWarning): # invalid escape sequence | | 779 | + with self.assertWarns(SyntaxWarning): # invalid escape sequence | 780 | 780 | value = eval(r"f'\{6*7}'") | 781 | 781 | self.assertEqual(value, '\\42') | 782 | 782 | self.assertEqual(f'\\{6*7}', '\\42') | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_string_literals.py +12-12Lines changed: 12 additions & 12 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -109,19 +109,19 @@ def test_eval_str_invalid_escape(self): | 109 | 109 | for b in range(1, 128): | 110 | 110 | if b in b"""\n\r"'01234567NU\\abfnrtuvx""": | 111 | 111 | continue | 112 | | - with self.assertWarns(DeprecationWarning): | | 112 | + with self.assertWarns(SyntaxWarning): | 113 | 113 | self.assertEqual(eval(r"'\%c'" % b), '\\' + chr(b)) | 114 | 114 |
| 115 | 115 | with warnings.catch_warnings(record=True) as w: | 116 | | - warnings.simplefilter('always', category=DeprecationWarning) | | 116 | + warnings.simplefilter('always', category=SyntaxWarning) | 117 | 117 | eval("'''\n\\z'''") | 118 | 118 | self.assertEqual(len(w), 1) | 119 | 119 | self.assertEqual(str(w[0].message), r"invalid escape sequence '\z'") | 120 | 120 | self.assertEqual(w[0].filename, '<string>') | 121 | 121 | self.assertEqual(w[0].lineno, 1) | 122 | 122 |
| 123 | 123 | with warnings.catch_warnings(record=True) as w: | 124 | | - warnings.simplefilter('error', category=DeprecationWarning) | | 124 | + warnings.simplefilter('error', category=SyntaxWarning) | 125 | 125 | with self.assertRaises(SyntaxError) as cm: | 126 | 126 | eval("'''\n\\z'''") | 127 | 127 | exc = cm.exception | @@ -133,11 +133,11 @@ def test_eval_str_invalid_escape(self): | 133 | 133 |
| 134 | 134 | def test_eval_str_invalid_octal_escape(self): | 135 | 135 | for i in range(0o400, 0o1000): | 136 | | - with self.assertWarns(DeprecationWarning): | | 136 | + with self.assertWarns(SyntaxWarning): | 137 | 137 | self.assertEqual(eval(r"'\%o'" % i), chr(i)) | 138 | 138 |
| 139 | 139 | with warnings.catch_warnings(record=True) as w: | 140 | | - warnings.simplefilter('always', category=DeprecationWarning) | | 140 | + warnings.simplefilter('always', category=SyntaxWarning) | 141 | 141 | eval("'''\n\\407'''") | 142 | 142 | self.assertEqual(len(w), 1) | 143 | 143 | self.assertEqual(str(w[0].message), | @@ -146,7 +146,7 @@ def test_eval_str_invalid_octal_escape(self): | 146 | 146 | self.assertEqual(w[0].lineno, 1) | 147 | 147 |
| 148 | 148 | with warnings.catch_warnings(record=True) as w: | 149 | | - warnings.simplefilter('error', category=DeprecationWarning) | | 149 | + warnings.simplefilter('error', category=SyntaxWarning) | 150 | 150 | with self.assertRaises(SyntaxError) as cm: | 151 | 151 | eval("'''\n\\407'''") | 152 | 152 | exc = cm.exception | @@ -186,19 +186,19 @@ def test_eval_bytes_invalid_escape(self): | 186 | 186 | for b in range(1, 128): | 187 | 187 | if b in b"""\n\r"'01234567\\abfnrtvx""": | 188 | 188 | continue | 189 | | - with self.assertWarns(DeprecationWarning): | | 189 | + with self.assertWarns(SyntaxWarning): | 190 | 190 | self.assertEqual(eval(r"b'\%c'" % b), b'\\' + bytes([b])) | 191 | 191 |
| 192 | 192 | with warnings.catch_warnings(record=True) as w: | 193 | | - warnings.simplefilter('always', category=DeprecationWarning) | | 193 | + warnings.simplefilter('always', category=SyntaxWarning) | 194 | 194 | eval("b'''\n\\z'''") | 195 | 195 | self.assertEqual(len(w), 1) | 196 | 196 | self.assertEqual(str(w[0].message), r"invalid escape sequence '\z'") | 197 | 197 | self.assertEqual(w[0].filename, '<string>') | 198 | 198 | self.assertEqual(w[0].lineno, 1) | 199 | 199 |
| 200 | 200 | with warnings.catch_warnings(record=True) as w: | 201 | | - warnings.simplefilter('error', category=DeprecationWarning) | | 201 | + warnings.simplefilter('error', category=SyntaxWarning) | 202 | 202 | with self.assertRaises(SyntaxError) as cm: | 203 | 203 | eval("b'''\n\\z'''") | 204 | 204 | exc = cm.exception | @@ -209,11 +209,11 @@ def test_eval_bytes_invalid_escape(self): | 209 | 209 |
| 210 | 210 | def test_eval_bytes_invalid_octal_escape(self): | 211 | 211 | for i in range(0o400, 0o1000): | 212 | | - with self.assertWarns(DeprecationWarning): | | 212 | + with self.assertWarns(SyntaxWarning): | 213 | 213 | self.assertEqual(eval(r"b'\%o'" % i), bytes([i & 0o377])) | 214 | 214 |
| 215 | 215 | with warnings.catch_warnings(record=True) as w: | 216 | | - warnings.simplefilter('always', category=DeprecationWarning) | | 216 | + warnings.simplefilter('always', category=SyntaxWarning) | 217 | 217 | eval("b'''\n\\407'''") | 218 | 218 | self.assertEqual(len(w), 1) | 219 | 219 | self.assertEqual(str(w[0].message), | @@ -222,7 +222,7 @@ def test_eval_bytes_invalid_octal_escape(self): | 222 | 222 | self.assertEqual(w[0].lineno, 1) | 223 | 223 |
| 224 | 224 | with warnings.catch_warnings(record=True) as w: | 225 | | - warnings.simplefilter('error', category=DeprecationWarning) | | 225 | + warnings.simplefilter('error', category=SyntaxWarning) | 226 | 226 | with self.assertRaises(SyntaxError) as cm: | 227 | 227 | eval("b'''\n\\407'''") | 228 | 228 | exc = cm.exception | |
Collapse file Copy file name to clipboard +7Lines changed: 7 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 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. |
Collapse file Copy file name to clipboard +4Lines changed: 4 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 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. |
Collapse file Copy file name to clipboardExpand all lines: Parser/string_parser.c +9-2Lines changed: 9 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -21,9 +21,16 @@ warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token | 21 | 21 | if (msg == NULL) { | 22 | 22 | return -1; | 23 | 23 | } | 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, | 25 | 32 | t->lineno, NULL, NULL) < 0) { | 26 | | - if (PyErr_ExceptionMatches(PyExc_DeprecationWarning)) { | | 33 | + if (PyErr_ExceptionMatches(category)) { | 27 | 34 | /* Replace the DeprecationWarning exception with a SyntaxError | 28 | 35 | to get a more accurate error report */ | 29 | 36 | PyErr_Clear(); | |
Collapse file Copy file name to clipboardExpand all lines: Tools/c-analyzer/c_parser/_state_machine.py +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -96,7 +96,7 @@ def parse(srclines): | 96 | 96 | # # end matched parens | 97 | 97 | # ''') | 98 | 98 |
| 99 | | -''' | | 99 | +r''' | 100 | 100 | # for loop | 101 | 101 | (?: | 102 | 102 | \s* \b for | |
|