gh-133306: Use \z instead of \Z in regular expressions in the stdlib (GH-133337)

This commit is contained in:
Serhiy Storchaka 2025-05-03 17:58:49 +03:00 committed by GitHub
parent add0ca9ea0
commit 84a08f8629
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 23 additions and 23 deletions

View file

@ -371,7 +371,7 @@ def _setoption(arg):
if message: if message:
message = re.escape(message) message = re.escape(message)
if module: if module:
module = re.escape(module) + r'\Z' module = re.escape(module) + r'\z'
if lineno: if lineno:
try: try:
lineno = int(lineno) lineno = int(lineno)

View file

@ -6096,7 +6096,7 @@ _parser = re.compile(r""" # A numeric string consists of:
(?P<diag>\d*) # with (possibly empty) diagnostic info. (?P<diag>\d*) # with (possibly empty) diagnostic info.
) )
# \s* # \s*
\Z \z
""", re.VERBOSE | re.IGNORECASE).match """, re.VERBOSE | re.IGNORECASE).match
_all_zeros = re.compile('0*$').match _all_zeros = re.compile('0*$').match
@ -6124,7 +6124,7 @@ _parse_format_specifier_regex = re.compile(r"""\A
(?P<thousands_sep>[,_])? (?P<thousands_sep>[,_])?
(?:\.(?P<precision>0|(?!0)\d+))? (?:\.(?P<precision>0|(?!0)\d+))?
(?P<type>[eEfFgGn%])? (?P<type>[eEfFgGn%])?
\Z \z
""", re.VERBOSE|re.DOTALL) """, re.VERBOSE|re.DOTALL)
del re del re

View file

@ -30,7 +30,7 @@ from io import StringIO
NLCRE = re.compile(r'\r\n|\r|\n') NLCRE = re.compile(r'\r\n|\r|\n')
NLCRE_bol = re.compile(r'(\r\n|\r|\n)') NLCRE_bol = re.compile(r'(\r\n|\r|\n)')
NLCRE_eol = re.compile(r'(\r\n|\r|\n)\Z') NLCRE_eol = re.compile(r'(\r\n|\r|\n)\z')
NLCRE_crack = re.compile(r'(\r\n|\r|\n)') NLCRE_crack = re.compile(r'(\r\n|\r|\n)')
# RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character # RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character
# except controls, SP, and ":". # except controls, SP, and ":".

View file

@ -64,7 +64,7 @@ _RATIONAL_FORMAT = re.compile(r"""
(?:\.(?P<decimal>\d*|\d+(_\d+)*))? # an optional fractional part (?:\.(?P<decimal>\d*|\d+(_\d+)*))? # an optional fractional part
(?:E(?P<exp>[-+]?\d+(_\d+)*))? # and optional exponent (?:E(?P<exp>[-+]?\d+(_\d+)*))? # and optional exponent
) )
\s*\Z # and optional whitespace to finish \s*\z # and optional whitespace to finish
""", re.VERBOSE | re.IGNORECASE) """, re.VERBOSE | re.IGNORECASE)

View file

@ -1350,7 +1350,7 @@ class PyShell(OutputWindow):
self.text.see("insert") self.text.see("insert")
self.text.undo_block_stop() self.text.undo_block_stop()
_last_newline_re = re.compile(r"[ \t]*(\n[ \t]*)?\Z") _last_newline_re = re.compile(r"[ \t]*(\n[ \t]*)?\z")
def runit(self): def runit(self):
index_before = self.text.index("end-2c") index_before = self.text.index("end-2c")
line = self.text.get("iomark", "end-1c") line = self.text.get("iomark", "end-1c")

View file

@ -14,7 +14,7 @@ STR_RGX_REPR = (
r'(, value:\d)?' r'(, value:\d)?'
r'(, waiters:\d+)?' r'(, waiters:\d+)?'
r'(, waiters:\d+\/\d+)?' # barrier r'(, waiters:\d+\/\d+)?' # barrier
r')\]>\Z' r')\]>\z'
) )
RGX_REPR = re.compile(STR_RGX_REPR) RGX_REPR = re.compile(STR_RGX_REPR)

View file

@ -300,7 +300,7 @@ class GCTests(unittest.TestCase):
# We're mostly just checking that this doesn't crash. # We're mostly just checking that this doesn't crash.
rc, stdout, stderr = assert_python_ok("-c", code) rc, stdout, stderr = assert_python_ok("-c", code)
self.assertEqual(rc, 0) self.assertEqual(rc, 0)
self.assertRegex(stdout, rb"""\A\s*func=<function at \S+>\s*\Z""") self.assertRegex(stdout, rb"""\A\s*func=<function at \S+>\s*\z""")
self.assertFalse(stderr) self.assertFalse(stderr)
@refcount_test @refcount_test

View file

@ -1001,7 +1001,7 @@ from not_a_module import symbol
expected_error = error + ( expected_error = error + (
rb" \(consider renaming '.*numpy.py' if it has the " rb" \(consider renaming '.*numpy.py' if it has the "
rb"same name as a library you intended to import\)\s+\Z" rb"same name as a library you intended to import\)\s+\z"
) )
popen = script_helper.spawn_python(os.path.join(tmp, "numpy.py")) popen = script_helper.spawn_python(os.path.join(tmp, "numpy.py"))
@ -1022,14 +1022,14 @@ from not_a_module import symbol
f.write("this_script_does_not_attempt_to_import_numpy = True") f.write("this_script_does_not_attempt_to_import_numpy = True")
expected_error = ( expected_error = (
rb"AttributeError: module 'numpy' has no attribute 'attr'\s+\Z" rb"AttributeError: module 'numpy' has no attribute 'attr'\s+\z"
) )
popen = script_helper.spawn_python('-c', 'import numpy; numpy.attr', cwd=tmp) popen = script_helper.spawn_python('-c', 'import numpy; numpy.attr', cwd=tmp)
stdout, stderr = popen.communicate() stdout, stderr = popen.communicate()
self.assertRegex(stdout, expected_error) self.assertRegex(stdout, expected_error)
expected_error = ( expected_error = (
rb"ImportError: cannot import name 'attr' from 'numpy' \(.*\)\s+\Z" rb"ImportError: cannot import name 'attr' from 'numpy' \(.*\)\s+\z"
) )
popen = script_helper.spawn_python('-c', 'from numpy import attr', cwd=tmp) popen = script_helper.spawn_python('-c', 'from numpy import attr', cwd=tmp)
stdout, stderr = popen.communicate() stdout, stderr = popen.communicate()

View file

@ -6740,7 +6740,7 @@ class TimedRotatingFileHandlerTest(BaseFileTest):
rotator = rotators[i] rotator = rotators[i]
candidates = rotator.getFilesToDelete() candidates = rotator.getFilesToDelete()
self.assertEqual(len(candidates), n_files - backupCount, candidates) self.assertEqual(len(candidates), n_files - backupCount, candidates)
matcher = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\Z") matcher = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\z")
for c in candidates: for c in candidates:
d, fn = os.path.split(c) d, fn = os.path.split(c)
self.assertStartsWith(fn, prefix+'.') self.assertStartsWith(fn, prefix+'.')

View file

@ -19,7 +19,7 @@ strtod_parser = re.compile(r""" # A numeric string consists of:
(?P<int>\d*) # having a (possibly empty) integer part (?P<int>\d*) # having a (possibly empty) integer part
(?:\.(?P<frac>\d*))? # followed by an optional fractional part (?:\.(?P<frac>\d*))? # followed by an optional fractional part
(?:E(?P<exp>[-+]?\d+))? # and an optional exponent (?:E(?P<exp>[-+]?\d+))? # and an optional exponent
\Z \z
""", re.VERBOSE | re.IGNORECASE).match """, re.VERBOSE | re.IGNORECASE).match
# Pure Python version of correctly rounded string->float conversion. # Pure Python version of correctly rounded string->float conversion.

View file

@ -65,7 +65,7 @@ class AbstractWidgetTest(AbstractTkTest):
orig = widget[name] orig = widget[name]
if errmsg is not None: if errmsg is not None:
errmsg = errmsg.format(re.escape(str(value))) errmsg = errmsg.format(re.escape(str(value)))
errmsg = fr'\A{errmsg}\Z' errmsg = fr'\A{errmsg}\z'
with self.assertRaisesRegex(tkinter.TclError, errmsg or ''): with self.assertRaisesRegex(tkinter.TclError, errmsg or ''):
widget[name] = value widget[name] = value
self.assertEqual(widget[name], orig) self.assertEqual(widget[name], orig)

View file

@ -490,7 +490,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
width = self.combo.winfo_width() width = self.combo.winfo_width()
x, y = width - 5, 5 x, y = width - 5, 5
if sys.platform != 'darwin': # there's no down arrow on macOS if sys.platform != 'darwin': # there's no down arrow on macOS
self.assertRegex(self.combo.identify(x, y), r'.*downarrow\Z') self.assertRegex(self.combo.identify(x, y), r'.*downarrow\z')
self.combo.event_generate('<Button-1>', x=x, y=y) self.combo.event_generate('<Button-1>', x=x, y=y)
self.combo.event_generate('<ButtonRelease-1>', x=x, y=y) self.combo.event_generate('<ButtonRelease-1>', x=x, y=y)
@ -1250,7 +1250,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
height = self.spin.winfo_height() height = self.spin.winfo_height()
x = width - 5 x = width - 5
y = height//2 - 5 y = height//2 - 5
self.assertRegex(self.spin.identify(x, y), r'.*uparrow\Z') self.assertRegex(self.spin.identify(x, y), r'.*uparrow\z')
self.spin.event_generate('<ButtonPress-1>', x=x, y=y) self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
self.spin.event_generate('<ButtonRelease-1>', x=x, y=y) self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
self.spin.update_idletasks() self.spin.update_idletasks()
@ -1260,7 +1260,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
height = self.spin.winfo_height() height = self.spin.winfo_height()
x = width - 5 x = width - 5
y = height//2 + 4 y = height//2 + 4
self.assertRegex(self.spin.identify(x, y), r'.*downarrow\Z') self.assertRegex(self.spin.identify(x, y), r'.*downarrow\z')
self.spin.event_generate('<ButtonPress-1>', x=x, y=y) self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
self.spin.event_generate('<ButtonRelease-1>', x=x, y=y) self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
self.spin.update_idletasks() self.spin.update_idletasks()

View file

@ -86,7 +86,7 @@ class TextWrapper:
-(?: (?<=%(lt)s{2}-) | (?<=%(lt)s-%(lt)s-)) -(?: (?<=%(lt)s{2}-) | (?<=%(lt)s-%(lt)s-))
(?= %(lt)s -? %(lt)s) (?= %(lt)s -? %(lt)s)
| # end of word | # end of word
(?=%(ws)s|\Z) (?=%(ws)s|\z)
| # em-dash | # em-dash
(?<=%(wp)s) (?=-{2,}\w) (?<=%(wp)s) (?=-{2,}\w)
) )
@ -107,7 +107,7 @@ class TextWrapper:
sentence_end_re = re.compile(r'[a-z]' # lowercase letter sentence_end_re = re.compile(r'[a-z]' # lowercase letter
r'[\.\!\?]' # sentence-ending punct. r'[\.\!\?]' # sentence-ending punct.
r'[\"\']?' # optional end-of-quote r'[\"\']?' # optional end-of-quote
r'\Z') # end of chunk r'\z') # end of chunk
def __init__(self, def __init__(self,
width=70, width=70,

View file

@ -132,7 +132,7 @@ ContStr = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
group("'", r'\\\r?\n'), group("'", r'\\\r?\n'),
StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*' + StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
group('"', r'\\\r?\n')) group('"', r'\\\r?\n'))
PseudoExtras = group(r'\\\r?\n|\Z', Comment, Triple) PseudoExtras = group(r'\\\r?\n|\z', Comment, Triple)
PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
# For a given string prefix plus quotes, endpats maps it to a regex # For a given string prefix plus quotes, endpats maps it to a regex

View file

@ -460,7 +460,7 @@ def _check_bracketed_netloc(netloc):
# https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ # https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/
def _check_bracketed_host(hostname): def _check_bracketed_host(hostname):
if hostname.startswith('v'): if hostname.startswith('v'):
if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", hostname): if not re.match(r"\Av[a-fA-F0-9]+\..+\z", hostname):
raise ValueError(f"IPvFuture address is invalid") raise ValueError(f"IPvFuture address is invalid")
else: else:
ip = ipaddress.ip_address(hostname) # Throws Value Error if not IPv6 or IPv4 ip = ipaddress.ip_address(hostname) # Throws Value Error if not IPv6 or IPv4

View file

@ -37,9 +37,9 @@ class Translator:
Apply '(?s:)' to create a non-matching group that Apply '(?s:)' to create a non-matching group that
matches newlines (valid on Unix). matches newlines (valid on Unix).
Append '\Z' to imply fullmatch even when match is used. Append '\z' to imply fullmatch even when match is used.
""" """
return rf'(?s:{pattern})\Z' return rf'(?s:{pattern})\z'
def match_dirs(self, pattern): def match_dirs(self, pattern):
""" """