diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 7c950bfd5b1fd5..ffaf024cc4afe0 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -965,13 +965,13 @@ form. Clear the regular expression cache. -.. exception:: error(msg, pattern=None, pos=None) +.. exception:: ReCompileError(msg, pattern=None, pos=None) Exception raised when a string passed to one of the functions here is not a - valid regular expression (for example, it might contain unmatched parentheses) - or when some other error occurs during compilation or matching. It is never an - error if a string contains no match for a pattern. The error instance has - the following additional attributes: + valid regular expression (for example, it might contain unmatched + parentheses) or when some other error occurs during compilation or matching. + It is never an error if a string contains no match for a pattern. The + ``ReCompileError`` instance has the following additional attributes: .. attribute:: msg @@ -996,6 +996,10 @@ form. .. versionchanged:: 3.5 Added additional attributes. + .. versionchanged:: 3.9 + ``ReCompileError`` was originally named ``error``; the latter is kept as an alias for + backward compatibility. + .. _re-objects: Regular Expression Objects diff --git a/Lib/idlelib/searchengine.py b/Lib/idlelib/searchengine.py index 911e7d4691cac1..058c2464946d0f 100644 --- a/Lib/idlelib/searchengine.py +++ b/Lib/idlelib/searchengine.py @@ -84,7 +84,7 @@ def getprog(self): flags = flags | re.IGNORECASE try: prog = re.compile(pat, flags) - except re.error as what: + except re.ReCompileError as what: args = what.args msg = args[0] col = args[1] if len(args) >= 2 else -1 diff --git a/Lib/pstats.py b/Lib/pstats.py index 4b419a8ecdb6c7..41b95b56c04eea 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -312,7 +312,7 @@ def eval_print_amount(self, sel, list, msg): if isinstance(sel, str): try: rex = re.compile(sel) - except re.error: + except re.ReCompileError: msg += " \n" % sel return new_list, msg new_list = [] diff --git a/Lib/re.py b/Lib/re.py index 8f1d55ddf7d69d..e539d1546dcc11 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -115,7 +115,8 @@ U UNICODE For compatibility only. Ignored for string patterns (it is the default), and forbidden for bytes patterns. -This module also defines an exception 'error'. +This module also defines exception 'ReCompileError', aliased to 'error' for +backward compatibility. """ @@ -135,7 +136,7 @@ "findall", "finditer", "compile", "purge", "template", "escape", "error", "Pattern", "Match", "A", "I", "L", "M", "S", "X", "U", "ASCII", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE", - "UNICODE", + "UNICODE", "ReCompileError" ] __version__ = "2.2.1" @@ -178,7 +179,7 @@ def __repr__(self): globals().update(RegexFlag.__members__) # sre exception -error = sre_compile.error +ReCompileError = error = sre_compile.ReCompileError # -------------------------------------------------------------------- # public interface diff --git a/Lib/sre_compile.py b/Lib/sre_compile.py index c6398bfb83a576..10ef64127ba95e 100644 --- a/Lib/sre_compile.py +++ b/Lib/sre_compile.py @@ -136,7 +136,7 @@ def _compile(code, pattern, flags): emit(ANY) elif op in REPEATING_CODES: if flags & SRE_FLAG_TEMPLATE: - raise error("internal: unsupported template operator %r" % (op,)) + raise ReCompileError(f"internal: unsupported template operator {op!r}") if _simple(av[2]): if op is MAX_REPEAT: emit(REPEAT_ONE) @@ -179,7 +179,7 @@ def _compile(code, pattern, flags): else: lo, hi = av[1].getwidth() if lo != hi: - raise error("look-behind requires fixed-width pattern") + raise ReCompileError("look-behind requires fixed-width pattern") emit(lo) # look behind _compile(code, av[1], flags) emit(SUCCESS) @@ -244,7 +244,7 @@ def _compile(code, pattern, flags): else: code[skipyes] = _len(code) - skipyes + 1 else: - raise error("internal: unsupported operand type %r" % (op,)) + raise ReCompileError(f"internal: unsupported operand type {op!r}") def _compile_charset(charset, flags, code): # compile charset subprogram @@ -270,7 +270,7 @@ def _compile_charset(charset, flags, code): else: emit(av) else: - raise error("internal: unsupported set operator %r" % (op,)) + raise ReCompileError(f"internal: unsupported set operator {op!r}") emit(FAILURE) def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): diff --git a/Lib/sre_constants.py b/Lib/sre_constants.py index 8e613cb3fa5dcb..1f66549d3bd15e 100644 --- a/Lib/sre_constants.py +++ b/Lib/sre_constants.py @@ -17,10 +17,10 @@ from _sre import MAXREPEAT, MAXGROUPS -# SRE standard exception (access as sre.error) +# SRE standard exception (access as sre.ReCompileError) # should this really be here? -class error(Exception): +class ReCompileError(Exception): """Exception raised for invalid regular expressions. Attributes: @@ -52,6 +52,8 @@ def __init__(self, msg, pattern=None, pos=None): self.lineno = self.colno = None super().__init__(msg) +# backward compatibility after renaming in 3.9 +error = ReCompileError class _NamedIntConstant(int): def __new__(cls, value, name): diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 4817d761a22df9..8cb657e81c7a5b 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -36,7 +36,7 @@ def recurse(actual, expect): recurse(actual, expect) def checkPatternError(self, pattern, errmsg, pos=None): - with self.assertRaises(re.error) as cm: + with self.assertRaises(re.ReCompileError) as cm: re.compile(pattern) with self.subTest(pattern=pattern): err = cm.exception @@ -45,7 +45,7 @@ def checkPatternError(self, pattern, errmsg, pos=None): self.assertEqual(err.pos, pos) def checkTemplateError(self, pattern, repl, string, errmsg, pos=None): - with self.assertRaises(re.error) as cm: + with self.assertRaises(re.ReCompileError) as cm: re.sub(pattern, repl, string) with self.subTest(pattern=pattern, repl=repl): err = cm.exception @@ -53,6 +53,9 @@ def checkTemplateError(self, pattern, repl, string, errmsg, pos=None): if pos is not None: self.assertEqual(err.pos, pos) + def test_error_is_ReCompileError_alias(self): + assert re.error is re.ReCompileError + def test_keep_buffer(self): # See bug 14212 b = bytearray(b'x') @@ -123,7 +126,7 @@ def test_basic_re_sub(self): (chr(9)+chr(10)+chr(11)+chr(13)+chr(12)+chr(7)+chr(8))) for c in 'cdehijklmopqsuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': with self.subTest(c): - with self.assertRaises(re.error): + with self.assertRaises(re.ReCompileError): self.assertEqual(re.sub('a', '\\' + c, 'a'), '\\' + c) self.assertEqual(re.sub(r'^\s*', 'X', 'test'), 'Xtest') @@ -689,10 +692,10 @@ def test_other_escapes(self): re.purge() # for warnings for c in 'ceghijklmopqyzCEFGHIJKLMNOPQRTVXY': with self.subTest(c): - self.assertRaises(re.error, re.compile, '\\%c' % c) + self.assertRaises(re.ReCompileError, re.compile, '\\%c' % c) for c in 'ceghijklmopqyzABCEFGHIJKLMNOPQRTVXYZ': with self.subTest(c): - self.assertRaises(re.error, re.compile, '[\\%c]' % c) + self.assertRaises(re.ReCompileError, re.compile, '[\\%c]' % c) def test_named_unicode_escapes(self): # test individual Unicode named escapes @@ -819,14 +822,14 @@ def test_lookbehind(self): self.assertIsNone(re.match(r'(?:(a)|(x))b(?<=(?(1)c|x))c', 'abc')) self.assertTrue(re.match(r'(?:(a)|(x))b(?<=(?(1)b|x))c', 'abc')) # Group used before defined. - self.assertRaises(re.error, re.compile, r'(a)b(?<=(?(2)b|x))(c)') + self.assertRaises(re.ReCompileError, re.compile, r'(a)b(?<=(?(2)b|x))(c)') self.assertIsNone(re.match(r'(a)b(?<=(?(1)c|x))(c)', 'abc')) self.assertTrue(re.match(r'(a)b(?<=(?(1)b|x))(c)', 'abc')) # Group defined in the same lookbehind pattern - self.assertRaises(re.error, re.compile, r'(a)b(?<=(.)\2)(c)') - self.assertRaises(re.error, re.compile, r'(a)b(?<=(?P.)(?P=a))(c)') - self.assertRaises(re.error, re.compile, r'(a)b(?<=(a)(?(2)b|x))(c)') - self.assertRaises(re.error, re.compile, r'(a)b(?<=(.)(?<=\2))(c)') + self.assertRaises(re.ReCompileError, re.compile, r'(a)b(?<=(.)\2)(c)') + self.assertRaises(re.ReCompileError, re.compile, r'(a)b(?<=(?P.)(?P=a))(c)') + self.assertRaises(re.ReCompileError, re.compile, r'(a)b(?<=(a)(?(2)b|x))(c)') + self.assertRaises(re.ReCompileError, re.compile, r'(a)b(?<=(.)(?<=\2))(c)') def test_ignore_case(self): self.assertEqual(re.match("abc", "ABC", re.I).group(0), "ABC") @@ -1151,8 +1154,8 @@ def test_sre_byte_literals(self): self.assertTrue(re.match((r"\x%02x" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"\x%02x0" % i).encode(), bytes([i])+b"0")) self.assertTrue(re.match((r"\x%02xz" % i).encode(), bytes([i])+b"z")) - self.assertRaises(re.error, re.compile, br"\u1234") - self.assertRaises(re.error, re.compile, br"\U00012345") + self.assertRaises(re.ReCompileError, re.compile, br"\u1234") + self.assertRaises(re.ReCompileError, re.compile, br"\U00012345") self.assertTrue(re.match(br"\0", b"\000")) self.assertTrue(re.match(br"\08", b"\0008")) self.assertTrue(re.match(br"\01", b"\001")) @@ -1174,8 +1177,8 @@ def test_sre_byte_class_literals(self): self.assertTrue(re.match((r"[\x%02x]" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"[\x%02x0]" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"[\x%02xz]" % i).encode(), bytes([i]))) - self.assertRaises(re.error, re.compile, br"[\u1234]") - self.assertRaises(re.error, re.compile, br"[\U00012345]") + self.assertRaises(re.ReCompileError, re.compile, br"[\u1234]") + self.assertRaises(re.ReCompileError, re.compile, br"[\U00012345]") self.checkPatternError(br"[\567]", r'octal escape value \567 outside of ' r'range 0-0o377', 1) @@ -1545,11 +1548,11 @@ def test_ascii_and_unicode_flag(self): self.assertIsNone(pat.match(b'\xe0')) # Incompatibilities self.assertRaises(ValueError, re.compile, br'\w', re.UNICODE) - self.assertRaises(re.error, re.compile, br'(?u)\w') + self.assertRaises(re.ReCompileError, re.compile, br'(?u)\w') self.assertRaises(ValueError, re.compile, r'\w', re.UNICODE | re.ASCII) self.assertRaises(ValueError, re.compile, r'(?u)\w', re.ASCII) self.assertRaises(ValueError, re.compile, r'(?a)\w', re.UNICODE) - self.assertRaises(re.error, re.compile, r'(?au)\w') + self.assertRaises(re.ReCompileError, re.compile, r'(?au)\w') def test_locale_flag(self): enc = locale.getpreferredencoding() @@ -1590,11 +1593,11 @@ def test_locale_flag(self): self.assertIsNone(pat.match(bletter)) # Incompatibilities self.assertRaises(ValueError, re.compile, '', re.LOCALE) - self.assertRaises(re.error, re.compile, '(?L)') + self.assertRaises(re.ReCompileError, re.compile, '(?L)') self.assertRaises(ValueError, re.compile, b'', re.LOCALE | re.ASCII) self.assertRaises(ValueError, re.compile, b'(?L)', re.ASCII) self.assertRaises(ValueError, re.compile, b'(?a)', re.LOCALE) - self.assertRaises(re.error, re.compile, b'(?aL)') + self.assertRaises(re.ReCompileError, re.compile, b'(?aL)') def test_scoped_flags(self): self.assertTrue(re.match(r'(?i:a)b', 'Ab')) @@ -1960,7 +1963,7 @@ def test_locale_compiled(self): self.assertIsNone(p4.match(b'\xc5\xc5')) def test_error(self): - with self.assertRaises(re.error) as cm: + with self.assertRaises(re.ReCompileError) as cm: re.compile('(\u20ac))') err = cm.exception self.assertIsInstance(err.pattern, str) @@ -1972,14 +1975,14 @@ def test_error(self): self.assertIn(' at position 3', str(err)) self.assertNotIn(' at position 3', err.msg) # Bytes pattern - with self.assertRaises(re.error) as cm: + with self.assertRaises(re.ReCompileError) as cm: re.compile(b'(\xa4))') err = cm.exception self.assertIsInstance(err.pattern, bytes) self.assertEqual(err.pattern, b'(\xa4))') self.assertEqual(err.pos, 3) # Multiline pattern - with self.assertRaises(re.error) as cm: + with self.assertRaises(re.ReCompileError) as cm: re.compile(""" ( abc @@ -2229,7 +2232,7 @@ def test_re_tests(self): with self.subTest(pattern=pattern, string=s): if outcome == SYNTAX_ERROR: # Expected a syntax error - with self.assertRaises(re.error): + with self.assertRaises(re.ReCompileError): re.compile(pattern) continue diff --git a/Misc/NEWS.d/next/Library/2019-12-07-13-19-03.bpo-38981.kJrSbj.rst b/Misc/NEWS.d/next/Library/2019-12-07-13-19-03.bpo-38981.kJrSbj.rst new file mode 100644 index 00000000000000..775d8c8e537811 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-12-07-13-19-03.bpo-38981.kJrSbj.rst @@ -0,0 +1,3 @@ +The ``re.error`` exception has been renamed to :exc:`ReCompileError` for +clarity and conformance with pep-8. A ``re.error`` alias has been kept for +backward compatibility. diff --git a/Modules/_xxtestfuzz/fuzzer.c b/Modules/_xxtestfuzz/fuzzer.c index dae1eaeabd000b..2e48ecba025123 100644 --- a/Modules/_xxtestfuzz/fuzzer.c +++ b/Modules/_xxtestfuzz/fuzzer.c @@ -130,7 +130,7 @@ PyObject* sre_error_exception = NULL; int SRE_FLAG_DEBUG = 0; /* Called by LLVMFuzzerTestOneInput for initialization */ static int init_sre_compile() { - /* Import sre_compile.compile and sre.error */ + /* Import sre_compile.compile and sre_constants.ReCompileError */ PyObject* sre_compile_module = PyImport_ImportModule("sre_compile"); if (sre_compile_module == NULL) { return 0; @@ -144,7 +144,7 @@ static int init_sre_compile() { if (sre_constants == NULL) { return 0; } - sre_error_exception = PyObject_GetAttrString(sre_constants, "error"); + sre_error_exception = PyObject_GetAttrString(sre_constants, "ReCompileError"); if (sre_error_exception == NULL) { return 0; } @@ -197,7 +197,7 @@ static int fuzz_sre_compile(const char* data, size_t size) { ) { PyErr_Clear(); } - /* Ignore re.error */ + /* Ignore re.ReCompileError */ if (compiled == NULL && PyErr_ExceptionMatches(sre_error_exception)) { PyErr_Clear(); } diff --git a/Tools/demo/redemo.py b/Tools/demo/redemo.py index f801dfce5fe1a3..e3a08bc8366df5 100755 --- a/Tools/demo/redemo.py +++ b/Tools/demo/redemo.py @@ -104,10 +104,10 @@ def recompile(self, event=None): self.getflags()) bg = self.promptdisplay['background'] self.statusdisplay.config(text="", background=bg) - except re.error as msg: + except re.ReCompileError as msg: self.compiled = None self.statusdisplay.config( - text="re.error: %s" % str(msg), + text="re.ReCompileError: %s" % str(msg), background="red") self.reevaluate() diff --git a/Tools/scripts/rgrep.py b/Tools/scripts/rgrep.py index c39bf93aad3bb2..0ce9422bae1f5d 100755 --- a/Tools/scripts/rgrep.py +++ b/Tools/scripts/rgrep.py @@ -24,7 +24,7 @@ def main(): pattern, filename = args try: prog = re.compile(pattern, reflags) - except re.error as msg: + except re.ReCompileError as msg: usage("error in regular expression: %s" % msg) try: f = open(filename)