Skip to content

Commit

Permalink
GH-83162: Rename re.error for better clarity. (#101677)
Browse files Browse the repository at this point in the history
Renamed re.error for clarity, and kept re.error for backward compatibility.
Updated idlelib files at TJR's request.
---------

Co-authored-by: Matthias Bussonnier <[email protected]>
Co-authored-by: Hugo van Kemenade <[email protected]>
Co-authored-by: Terry Jan Reedy <[email protected]>
  • Loading branch information
4 people authored Dec 11, 2023
1 parent 0066ab5 commit a01022a
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 34 deletions.
8 changes: 6 additions & 2 deletions Doc/library/re.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1093,12 +1093,12 @@ Functions
Exceptions
^^^^^^^^^^

.. exception:: error(msg, pattern=None, pos=None)
.. exception:: PatternError(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
error if a string contains no match for a pattern. The ``PatternError`` instance has
the following additional attributes:

.. attribute:: msg
Expand All @@ -1124,6 +1124,10 @@ Exceptions
.. versionchanged:: 3.5
Added additional attributes.

.. versionchanged:: 3.13
``PatternError`` was originally named ``error``; the latter is kept as an alias for
backward compatibility.

.. _re-objects:

Regular Expression Objects
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ pdb
command line option or :envvar:`PYTHONSAFEPATH` environment variable).
(Contributed by Tian Gao and Christian Walther in :gh:`111762`.)

re
--
* Rename :exc:`!re.error` to :exc:`re.PatternError` for improved clarity.
:exc:`!re.error` is kept for backward compatibility.

sqlite3
-------

Expand Down
2 changes: 1 addition & 1 deletion Lib/idlelib/replace.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def _replace_expand(self, m, repl):
if self.engine.isre():
try:
new = m.expand(repl)
except re.error:
except re.PatternError:
self.engine.report_error(repl, 'Invalid Replace Expression')
new = None
else:
Expand Down
2 changes: 1 addition & 1 deletion Lib/idlelib/searchengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def getprog(self):
flags = flags | re.IGNORECASE
try:
prog = re.compile(pat, flags)
except re.error as e:
except re.PatternError as e:
self.report_error(pat, e.msg, e.pos)
return None
return prog
Expand Down
2 changes: 1 addition & 1 deletion Lib/pstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def eval_print_amount(self, sel, list, msg):
if isinstance(sel, str):
try:
rex = re.compile(sel)
except re.error:
except re.PatternError:
msg += " <Invalid regular expression %r>\n" % sel
return new_list, msg
new_list = []
Expand Down
7 changes: 4 additions & 3 deletions Lib/re/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,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 'PatternError', aliased to 'error' for
backward compatibility.
"""

Expand All @@ -133,7 +134,7 @@
"findall", "finditer", "compile", "purge", "escape",
"error", "Pattern", "Match", "A", "I", "L", "M", "S", "X", "U",
"ASCII", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE",
"UNICODE", "NOFLAG", "RegexFlag",
"UNICODE", "NOFLAG", "RegexFlag", "PatternError"
]

__version__ = "2.2.1"
Expand All @@ -155,7 +156,7 @@ class RegexFlag:
_numeric_repr_ = hex

# sre exception
error = _compiler.error
PatternError = error = _compiler.PatternError

# --------------------------------------------------------------------
# public interface
Expand Down
6 changes: 3 additions & 3 deletions Lib/re/_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def _compile(code, pattern, flags):
if lo > MAXCODE:
raise error("looks too much behind")
if lo != hi:
raise error("look-behind requires fixed-width pattern")
raise PatternError("look-behind requires fixed-width pattern")
emit(lo) # look behind
_compile(code, av[1], flags)
emit(SUCCESS)
Expand Down Expand Up @@ -209,7 +209,7 @@ def _compile(code, pattern, flags):
else:
code[skipyes] = _len(code) - skipyes + 1
else:
raise error("internal: unsupported operand type %r" % (op,))
raise PatternError(f"internal: unsupported operand type {op!r}")

def _compile_charset(charset, flags, code):
# compile charset subprogram
Expand All @@ -235,7 +235,7 @@ def _compile_charset(charset, flags, code):
else:
emit(av)
else:
raise error("internal: unsupported set operator %r" % (op,))
raise PatternError(f"internal: unsupported set operator {op!r}")
emit(FAILURE)

def _optimize_charset(charset, iscased=None, fixup=None, fixes=None):
Expand Down
5 changes: 4 additions & 1 deletion Lib/re/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# SRE standard exception (access as sre.error)
# should this really be here?

class error(Exception):
class PatternError(Exception):
"""Exception raised for invalid regular expressions.
Attributes:
Expand Down Expand Up @@ -53,6 +53,9 @@ def __init__(self, msg, pattern=None, pos=None):
super().__init__(msg)


# Backward compatibility after renaming in 3.13
error = PatternError

class _NamedIntConstant(int):
def __new__(cls, value, name):
self = super(_NamedIntConstant, cls).__new__(cls, value)
Expand Down
47 changes: 25 additions & 22 deletions Lib/test/test_re.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,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.PatternError) as cm:
re.compile(pattern)
with self.subTest(pattern=pattern):
err = cm.exception
Expand All @@ -56,14 +56,17 @@ 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.PatternError) as cm:
re.sub(pattern, repl, string)
with self.subTest(pattern=pattern, repl=repl):
err = cm.exception
self.assertEqual(err.msg, errmsg)
if pos is not None:
self.assertEqual(err.pos, pos)

def test_error_is_PatternError_alias(self):
assert re.error is re.PatternError

def test_keep_buffer(self):
# See bug 14212
b = bytearray(b'x')
Expand Down Expand Up @@ -154,7 +157,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.PatternError):
self.assertEqual(re.sub('a', '\\' + c, 'a'), '\\' + c)

self.assertEqual(re.sub(r'^\s*', 'X', 'test'), 'Xtest')
Expand Down Expand Up @@ -836,10 +839,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.PatternError, re.compile, '\\%c' % c)
for c in 'ceghijklmopqyzABCEFGHIJKLMNOPQRTVXYZ':
with self.subTest(c):
self.assertRaises(re.error, re.compile, '[\\%c]' % c)
self.assertRaises(re.PatternError, re.compile, '[\\%c]' % c)

def test_named_unicode_escapes(self):
# test individual Unicode named escapes
Expand Down Expand Up @@ -970,14 +973,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.PatternError, 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<a>.)(?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.PatternError, re.compile, r'(a)b(?<=(.)\2)(c)')
self.assertRaises(re.PatternError, re.compile, r'(a)b(?<=(?P<a>.)(?P=a))(c)')
self.assertRaises(re.PatternError, re.compile, r'(a)b(?<=(a)(?(2)b|x))(c)')
self.assertRaises(re.PatternError, re.compile, r'(a)b(?<=(.)(?<=\2))(c)')

def test_ignore_case(self):
self.assertEqual(re.match("abc", "ABC", re.I).group(0), "ABC")
Expand Down Expand Up @@ -1318,8 +1321,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.PatternError, re.compile, br"\u1234")
self.assertRaises(re.PatternError, 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"))
Expand All @@ -1341,8 +1344,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.PatternError, re.compile, br"[\u1234]")
self.assertRaises(re.PatternError, re.compile, br"[\U00012345]")
self.checkPatternError(br"[\567]",
r'octal escape value \567 outside of '
r'range 0-0o377', 1)
Expand Down Expand Up @@ -1675,11 +1678,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.PatternError, 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.PatternError, re.compile, r'(?au)\w')

def test_locale_flag(self):
enc = locale.getpreferredencoding()
Expand Down Expand Up @@ -1720,11 +1723,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.PatternError, 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.PatternError, re.compile, b'(?aL)')

def test_scoped_flags(self):
self.assertTrue(re.match(r'(?i:a)b', 'Ab'))
Expand Down Expand Up @@ -2060,7 +2063,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.PatternError) as cm:
re.compile('(\u20ac))')
err = cm.exception
self.assertIsInstance(err.pattern, str)
Expand All @@ -2072,14 +2075,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.PatternError) 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.PatternError) as cm:
re.compile("""
(
abc
Expand Down Expand Up @@ -2820,7 +2823,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.PatternError):
re.compile(pattern)
continue

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Renamed :exc:`!re.error` to :exc:`PatternError` for clarity, and kept
:exc:`!re.error` for backward compatibility. Patch by Matthias Bussonnier and
Adam Chhina.

0 comments on commit a01022a

Please sign in to comment.