diff options
-rw-r--r-- | Doc/library/fnmatch.rst | 2 | ||||
-rw-r--r-- | Doc/library/glob.rst | 2 | ||||
-rw-r--r-- | Lib/fnmatch.py | 4 | ||||
-rw-r--r-- | Lib/glob.py | 2 | ||||
-rw-r--r-- | Lib/test/test_fnmatch.py | 98 | ||||
-rw-r--r-- | Lib/test/test_glob.py | 80 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2025-05-03-13-19-22.gh-issue-133306.ustKV3.rst | 2 |
7 files changed, 96 insertions, 94 deletions
diff --git a/Doc/library/fnmatch.rst b/Doc/library/fnmatch.rst index 8674e855b8e..12e61bc36f5 100644 --- a/Doc/library/fnmatch.rst +++ b/Doc/library/fnmatch.rst @@ -111,7 +111,7 @@ functions: :func:`fnmatch`, :func:`fnmatchcase`, :func:`.filter`. >>> >>> regex = fnmatch.translate('*.txt') >>> regex - '(?s:.*\\.txt)\\Z' + '(?s:.*\\.txt)\\z' >>> reobj = re.compile(regex) >>> reobj.match('foobar.txt') <re.Match object; span=(0, 10), match='foobar.txt'> diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst index 684466d354a..59ad1b07f27 100644 --- a/Doc/library/glob.rst +++ b/Doc/library/glob.rst @@ -134,7 +134,7 @@ The :mod:`glob` module defines the following functions: >>> >>> regex = glob.translate('**/*.txt', recursive=True, include_hidden=True) >>> regex - '(?s:(?:.+/)?[^/]*\\.txt)\\Z' + '(?s:(?:.+/)?[^/]*\\.txt)\\z' >>> reobj = re.compile(regex) >>> reobj.match('foo/bar/baz.txt') <re.Match object; span=(0, 15), match='foo/bar/baz.txt'> diff --git a/Lib/fnmatch.py b/Lib/fnmatch.py index 1dee8330f5d..10e1c936688 100644 --- a/Lib/fnmatch.py +++ b/Lib/fnmatch.py @@ -185,7 +185,7 @@ def _translate(pat, star, question_mark): def _join_translated_parts(parts, star_indices): if not star_indices: - return fr'(?s:{"".join(parts)})\Z' + return fr'(?s:{"".join(parts)})\z' iter_star_indices = iter(star_indices) j = next(iter_star_indices) buffer = parts[:j] # fixed pieces at the start @@ -206,4 +206,4 @@ def _join_translated_parts(parts, star_indices): append('.*') extend(parts[i:]) res = ''.join(buffer) - return fr'(?s:{res})\Z' + return fr'(?s:{res})\z' diff --git a/Lib/glob.py b/Lib/glob.py index 8879eff8041..341524282ba 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -316,7 +316,7 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None): if idx < last_part_idx: results.append(any_sep) res = ''.join(results) - return fr'(?s:{res})\Z' + return fr'(?s:{res})\z' @functools.lru_cache(maxsize=512) diff --git a/Lib/test/test_fnmatch.py b/Lib/test/test_fnmatch.py index d4163cfe782..5daaf3b3fdd 100644 --- a/Lib/test/test_fnmatch.py +++ b/Lib/test/test_fnmatch.py @@ -218,24 +218,24 @@ class TranslateTestCase(unittest.TestCase): def test_translate(self): import re - self.assertEqual(translate('*'), r'(?s:.*)\Z') - self.assertEqual(translate('?'), r'(?s:.)\Z') - self.assertEqual(translate('a?b*'), r'(?s:a.b.*)\Z') - self.assertEqual(translate('[abc]'), r'(?s:[abc])\Z') - self.assertEqual(translate('[]]'), r'(?s:[]])\Z') - self.assertEqual(translate('[!x]'), r'(?s:[^x])\Z') - self.assertEqual(translate('[^x]'), r'(?s:[\^x])\Z') - self.assertEqual(translate('[x'), r'(?s:\[x)\Z') + self.assertEqual(translate('*'), r'(?s:.*)\z') + self.assertEqual(translate('?'), r'(?s:.)\z') + self.assertEqual(translate('a?b*'), r'(?s:a.b.*)\z') + self.assertEqual(translate('[abc]'), r'(?s:[abc])\z') + self.assertEqual(translate('[]]'), r'(?s:[]])\z') + self.assertEqual(translate('[!x]'), r'(?s:[^x])\z') + self.assertEqual(translate('[^x]'), r'(?s:[\^x])\z') + self.assertEqual(translate('[x'), r'(?s:\[x)\z') # from the docs - self.assertEqual(translate('*.txt'), r'(?s:.*\.txt)\Z') + self.assertEqual(translate('*.txt'), r'(?s:.*\.txt)\z') # squash consecutive stars - self.assertEqual(translate('*********'), r'(?s:.*)\Z') - self.assertEqual(translate('A*********'), r'(?s:A.*)\Z') - self.assertEqual(translate('*********A'), r'(?s:.*A)\Z') - self.assertEqual(translate('A*********?[?]?'), r'(?s:A.*.[?].)\Z') + self.assertEqual(translate('*********'), r'(?s:.*)\z') + self.assertEqual(translate('A*********'), r'(?s:A.*)\z') + self.assertEqual(translate('*********A'), r'(?s:.*A)\z') + self.assertEqual(translate('A*********?[?]?'), r'(?s:A.*.[?].)\z') # fancy translation to prevent exponential-time match failure t = translate('**a*a****a') - self.assertEqual(t, r'(?s:(?>.*?a)(?>.*?a).*a)\Z') + self.assertEqual(t, r'(?s:(?>.*?a)(?>.*?a).*a)\z') # and try pasting multiple translate results - it's an undocumented # feature that this works r1 = translate('**a**a**a*') @@ -249,27 +249,27 @@ class TranslateTestCase(unittest.TestCase): def test_translate_wildcards(self): for pattern, expect in [ - ('ab*', r'(?s:ab.*)\Z'), - ('ab*cd', r'(?s:ab.*cd)\Z'), - ('ab*cd*', r'(?s:ab(?>.*?cd).*)\Z'), - ('ab*cd*12', r'(?s:ab(?>.*?cd).*12)\Z'), - ('ab*cd*12*', r'(?s:ab(?>.*?cd)(?>.*?12).*)\Z'), - ('ab*cd*12*34', r'(?s:ab(?>.*?cd)(?>.*?12).*34)\Z'), - ('ab*cd*12*34*', r'(?s:ab(?>.*?cd)(?>.*?12)(?>.*?34).*)\Z'), + ('ab*', r'(?s:ab.*)\z'), + ('ab*cd', r'(?s:ab.*cd)\z'), + ('ab*cd*', r'(?s:ab(?>.*?cd).*)\z'), + ('ab*cd*12', r'(?s:ab(?>.*?cd).*12)\z'), + ('ab*cd*12*', r'(?s:ab(?>.*?cd)(?>.*?12).*)\z'), + ('ab*cd*12*34', r'(?s:ab(?>.*?cd)(?>.*?12).*34)\z'), + ('ab*cd*12*34*', r'(?s:ab(?>.*?cd)(?>.*?12)(?>.*?34).*)\z'), ]: with self.subTest(pattern): translated = translate(pattern) self.assertEqual(translated, expect, pattern) for pattern, expect in [ - ('*ab', r'(?s:.*ab)\Z'), - ('*ab*', r'(?s:(?>.*?ab).*)\Z'), - ('*ab*cd', r'(?s:(?>.*?ab).*cd)\Z'), - ('*ab*cd*', r'(?s:(?>.*?ab)(?>.*?cd).*)\Z'), - ('*ab*cd*12', r'(?s:(?>.*?ab)(?>.*?cd).*12)\Z'), - ('*ab*cd*12*', r'(?s:(?>.*?ab)(?>.*?cd)(?>.*?12).*)\Z'), - ('*ab*cd*12*34', r'(?s:(?>.*?ab)(?>.*?cd)(?>.*?12).*34)\Z'), - ('*ab*cd*12*34*', r'(?s:(?>.*?ab)(?>.*?cd)(?>.*?12)(?>.*?34).*)\Z'), + ('*ab', r'(?s:.*ab)\z'), + ('*ab*', r'(?s:(?>.*?ab).*)\z'), + ('*ab*cd', r'(?s:(?>.*?ab).*cd)\z'), + ('*ab*cd*', r'(?s:(?>.*?ab)(?>.*?cd).*)\z'), + ('*ab*cd*12', r'(?s:(?>.*?ab)(?>.*?cd).*12)\z'), + ('*ab*cd*12*', r'(?s:(?>.*?ab)(?>.*?cd)(?>.*?12).*)\z'), + ('*ab*cd*12*34', r'(?s:(?>.*?ab)(?>.*?cd)(?>.*?12).*34)\z'), + ('*ab*cd*12*34*', r'(?s:(?>.*?ab)(?>.*?cd)(?>.*?12)(?>.*?34).*)\z'), ]: with self.subTest(pattern): translated = translate(pattern) @@ -277,28 +277,28 @@ class TranslateTestCase(unittest.TestCase): def test_translate_expressions(self): for pattern, expect in [ - ('[', r'(?s:\[)\Z'), - ('[!', r'(?s:\[!)\Z'), - ('[]', r'(?s:\[\])\Z'), - ('[abc', r'(?s:\[abc)\Z'), - ('[!abc', r'(?s:\[!abc)\Z'), - ('[abc]', r'(?s:[abc])\Z'), - ('[!abc]', r'(?s:[^abc])\Z'), - ('[!abc][!def]', r'(?s:[^abc][^def])\Z'), + ('[', r'(?s:\[)\z'), + ('[!', r'(?s:\[!)\z'), + ('[]', r'(?s:\[\])\z'), + ('[abc', r'(?s:\[abc)\z'), + ('[!abc', r'(?s:\[!abc)\z'), + ('[abc]', r'(?s:[abc])\z'), + ('[!abc]', r'(?s:[^abc])\z'), + ('[!abc][!def]', r'(?s:[^abc][^def])\z'), # with [[ - ('[[', r'(?s:\[\[)\Z'), - ('[[a', r'(?s:\[\[a)\Z'), - ('[[]', r'(?s:[\[])\Z'), - ('[[]a', r'(?s:[\[]a)\Z'), - ('[[]]', r'(?s:[\[]\])\Z'), - ('[[]a]', r'(?s:[\[]a\])\Z'), - ('[[a]', r'(?s:[\[a])\Z'), - ('[[a]]', r'(?s:[\[a]\])\Z'), - ('[[a]b', r'(?s:[\[a]b)\Z'), + ('[[', r'(?s:\[\[)\z'), + ('[[a', r'(?s:\[\[a)\z'), + ('[[]', r'(?s:[\[])\z'), + ('[[]a', r'(?s:[\[]a)\z'), + ('[[]]', r'(?s:[\[]\])\z'), + ('[[]a]', r'(?s:[\[]a\])\z'), + ('[[a]', r'(?s:[\[a])\z'), + ('[[a]]', r'(?s:[\[a]\])\z'), + ('[[a]b', r'(?s:[\[a]b)\z'), # backslashes - ('[\\', r'(?s:\[\\)\Z'), - (r'[\]', r'(?s:[\\])\Z'), - (r'[\\]', r'(?s:[\\\\])\Z'), + ('[\\', r'(?s:\[\\)\z'), + (r'[\]', r'(?s:[\\])\z'), + (r'[\\]', r'(?s:[\\\\])\z'), ]: with self.subTest(pattern): translated = translate(pattern) diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 6e5fc2939c6..d0ed5129253 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -459,59 +459,59 @@ class GlobTests(unittest.TestCase): def test_translate(self): def fn(pat): return glob.translate(pat, seps='/') - self.assertEqual(fn('foo'), r'(?s:foo)\Z') - self.assertEqual(fn('foo/bar'), r'(?s:foo/bar)\Z') - self.assertEqual(fn('*'), r'(?s:[^/.][^/]*)\Z') - self.assertEqual(fn('?'), r'(?s:(?!\.)[^/])\Z') - self.assertEqual(fn('a*'), r'(?s:a[^/]*)\Z') - self.assertEqual(fn('*a'), r'(?s:(?!\.)[^/]*a)\Z') - self.assertEqual(fn('.*'), r'(?s:\.[^/]*)\Z') - self.assertEqual(fn('?aa'), r'(?s:(?!\.)[^/]aa)\Z') - self.assertEqual(fn('aa?'), r'(?s:aa[^/])\Z') - self.assertEqual(fn('aa[ab]'), r'(?s:aa[ab])\Z') - self.assertEqual(fn('**'), r'(?s:(?!\.)[^/]*)\Z') - self.assertEqual(fn('***'), r'(?s:(?!\.)[^/]*)\Z') - self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z') - self.assertEqual(fn('**b'), r'(?s:(?!\.)[^/]*b)\Z') + self.assertEqual(fn('foo'), r'(?s:foo)\z') + self.assertEqual(fn('foo/bar'), r'(?s:foo/bar)\z') + self.assertEqual(fn('*'), r'(?s:[^/.][^/]*)\z') + self.assertEqual(fn('?'), r'(?s:(?!\.)[^/])\z') + self.assertEqual(fn('a*'), r'(?s:a[^/]*)\z') + self.assertEqual(fn('*a'), r'(?s:(?!\.)[^/]*a)\z') + self.assertEqual(fn('.*'), r'(?s:\.[^/]*)\z') + self.assertEqual(fn('?aa'), r'(?s:(?!\.)[^/]aa)\z') + self.assertEqual(fn('aa?'), r'(?s:aa[^/])\z') + self.assertEqual(fn('aa[ab]'), r'(?s:aa[ab])\z') + self.assertEqual(fn('**'), r'(?s:(?!\.)[^/]*)\z') + self.assertEqual(fn('***'), r'(?s:(?!\.)[^/]*)\z') + self.assertEqual(fn('a**'), r'(?s:a[^/]*)\z') + self.assertEqual(fn('**b'), r'(?s:(?!\.)[^/]*b)\z') self.assertEqual(fn('/**/*/*.*/**'), - r'(?s:/(?!\.)[^/]*/[^/.][^/]*/(?!\.)[^/]*\.[^/]*/(?!\.)[^/]*)\Z') + r'(?s:/(?!\.)[^/]*/[^/.][^/]*/(?!\.)[^/]*\.[^/]*/(?!\.)[^/]*)\z') def test_translate_include_hidden(self): def fn(pat): return glob.translate(pat, include_hidden=True, seps='/') - self.assertEqual(fn('foo'), r'(?s:foo)\Z') - self.assertEqual(fn('foo/bar'), r'(?s:foo/bar)\Z') - self.assertEqual(fn('*'), r'(?s:[^/]+)\Z') - self.assertEqual(fn('?'), r'(?s:[^/])\Z') - self.assertEqual(fn('a*'), r'(?s:a[^/]*)\Z') - self.assertEqual(fn('*a'), r'(?s:[^/]*a)\Z') - self.assertEqual(fn('.*'), r'(?s:\.[^/]*)\Z') - self.assertEqual(fn('?aa'), r'(?s:[^/]aa)\Z') - self.assertEqual(fn('aa?'), r'(?s:aa[^/])\Z') - self.assertEqual(fn('aa[ab]'), r'(?s:aa[ab])\Z') - self.assertEqual(fn('**'), r'(?s:[^/]*)\Z') - self.assertEqual(fn('***'), r'(?s:[^/]*)\Z') - self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z') - self.assertEqual(fn('**b'), r'(?s:[^/]*b)\Z') - self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/[^/]*/[^/]+/[^/]*\.[^/]*/[^/]*)\Z') + self.assertEqual(fn('foo'), r'(?s:foo)\z') + self.assertEqual(fn('foo/bar'), r'(?s:foo/bar)\z') + self.assertEqual(fn('*'), r'(?s:[^/]+)\z') + self.assertEqual(fn('?'), r'(?s:[^/])\z') + self.assertEqual(fn('a*'), r'(?s:a[^/]*)\z') + self.assertEqual(fn('*a'), r'(?s:[^/]*a)\z') + self.assertEqual(fn('.*'), r'(?s:\.[^/]*)\z') + self.assertEqual(fn('?aa'), r'(?s:[^/]aa)\z') + self.assertEqual(fn('aa?'), r'(?s:aa[^/])\z') + self.assertEqual(fn('aa[ab]'), r'(?s:aa[ab])\z') + self.assertEqual(fn('**'), r'(?s:[^/]*)\z') + self.assertEqual(fn('***'), r'(?s:[^/]*)\z') + self.assertEqual(fn('a**'), r'(?s:a[^/]*)\z') + self.assertEqual(fn('**b'), r'(?s:[^/]*b)\z') + self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/[^/]*/[^/]+/[^/]*\.[^/]*/[^/]*)\z') def test_translate_recursive(self): def fn(pat): return glob.translate(pat, recursive=True, include_hidden=True, seps='/') - self.assertEqual(fn('*'), r'(?s:[^/]+)\Z') - self.assertEqual(fn('?'), r'(?s:[^/])\Z') - self.assertEqual(fn('**'), r'(?s:.*)\Z') - self.assertEqual(fn('**/**'), r'(?s:.*)\Z') - self.assertEqual(fn('***'), r'(?s:[^/]*)\Z') - self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z') - self.assertEqual(fn('**b'), r'(?s:[^/]*b)\Z') - self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/(?:.+/)?[^/]+/[^/]*\.[^/]*/.*)\Z') + self.assertEqual(fn('*'), r'(?s:[^/]+)\z') + self.assertEqual(fn('?'), r'(?s:[^/])\z') + self.assertEqual(fn('**'), r'(?s:.*)\z') + self.assertEqual(fn('**/**'), r'(?s:.*)\z') + self.assertEqual(fn('***'), r'(?s:[^/]*)\z') + self.assertEqual(fn('a**'), r'(?s:a[^/]*)\z') + self.assertEqual(fn('**b'), r'(?s:[^/]*b)\z') + self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/(?:.+/)?[^/]+/[^/]*\.[^/]*/.*)\z') def test_translate_seps(self): def fn(pat): return glob.translate(pat, recursive=True, include_hidden=True, seps=['/', '\\']) - self.assertEqual(fn('foo/bar\\baz'), r'(?s:foo[/\\]bar[/\\]baz)\Z') - self.assertEqual(fn('**/*'), r'(?s:(?:.+[/\\])?[^/\\]+)\Z') + self.assertEqual(fn('foo/bar\\baz'), r'(?s:foo[/\\]bar[/\\]baz)\z') + self.assertEqual(fn('**/*'), r'(?s:(?:.+[/\\])?[^/\\]+)\z') if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Library/2025-05-03-13-19-22.gh-issue-133306.ustKV3.rst b/Misc/NEWS.d/next/Library/2025-05-03-13-19-22.gh-issue-133306.ustKV3.rst new file mode 100644 index 00000000000..5ec4860f553 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-03-13-19-22.gh-issue-133306.ustKV3.rst @@ -0,0 +1,2 @@ +Use ``\z`` instead of ``\Z`` in :func:`fnmatch.translate` and +:func:`glob.translate`. |