]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-116377: Stop raising `ValueError` from `glob.translate()`. (#116378)
authorBarney Gale <barney.gale@gmail.com>
Sun, 17 Mar 2024 17:09:35 +0000 (17:09 +0000)
committerGitHub <noreply@github.com>
Sun, 17 Mar 2024 17:09:35 +0000 (17:09 +0000)
Stop raising `ValueError` from `glob.translate()` when a `**` sub-string
appears in a non-recursive pattern segment. This matches `glob.glob()`
behaviour.

Doc/library/glob.rst
Lib/glob.py
Lib/test/test_glob.py
Lib/test/test_pathlib/test_pathlib_abc.py

index 19a0bbba8966bae055c72060b6b69d0cd9c3389b..15fef747296ed4fe32ebd2145103833f94491409 100644 (file)
@@ -136,8 +136,7 @@ The :mod:`glob` module defines the following functions:
    separators, and ``*`` pattern segments match precisely one path segment.
 
    If *recursive* is true, the pattern segment "``**``" will match any number
-   of path segments. If "``**``" occurs in any position other than a full
-   pattern segment, :exc:`ValueError` is raised.
+   of path segments.
 
    If *include_hidden* is true, wildcards can match path segments that start
    with a dot (``.``).
index 343be78a73b20a3eaa584c3b8b96eb0bc20ef4f1..473502c67336f97995a9f56471c411bcf5c373ff 100644 (file)
@@ -256,8 +256,7 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
     """Translate a pathname with shell wildcards to a regular expression.
 
     If `recursive` is true, the pattern segment '**' will match any number of
-    path segments; if '**' appears outside its own segment, ValueError will be
-    raised.
+    path segments.
 
     If `include_hidden` is true, wildcards can match path segments beginning
     with a dot ('.').
@@ -291,22 +290,18 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
     for idx, part in enumerate(parts):
         if part == '*':
             results.append(one_segment if idx < last_part_idx else one_last_segment)
-            continue
-        if recursive:
-            if part == '**':
-                if idx < last_part_idx:
-                    if parts[idx + 1] != '**':
-                        results.append(any_segments)
-                else:
-                    results.append(any_last_segments)
-                continue
-            elif '**' in part:
-                raise ValueError("Invalid pattern: '**' can only be an entire path component")
-        if part:
-            if not include_hidden and part[0] in '*?':
-                results.append(r'(?!\.)')
-            results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
-        if idx < last_part_idx:
-            results.append(any_sep)
+        elif recursive and part == '**':
+            if idx < last_part_idx:
+                if parts[idx + 1] != '**':
+                    results.append(any_segments)
+            else:
+                results.append(any_last_segments)
+        else:
+            if part:
+                if not include_hidden and part[0] in '*?':
+                    results.append(r'(?!\.)')
+                results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
+            if idx < last_part_idx:
+                results.append(any_sep)
     res = ''.join(results)
     return fr'(?s:{res})\Z'
index 1fdf2817e236f6128d196f054f3f0fcc9573dc46..2de997501039adeaa52e0cdbda855ff6ba909b69 100644 (file)
@@ -452,9 +452,9 @@ class GlobTests(unittest.TestCase):
         self.assertEqual(fn('?'), r'(?s:[^/])\Z')
         self.assertEqual(fn('**'), r'(?s:.*)\Z')
         self.assertEqual(fn('**/**'), r'(?s:.*)\Z')
-        self.assertRaises(ValueError, fn, '***')
-        self.assertRaises(ValueError, fn, 'a**')
-        self.assertRaises(ValueError, fn, '**b')
+        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):
index fb467a015a80d2fb0ce8ff676e6d17ee09c548dd..840fb903fd53383e8a81f96429f649c2f1cb34e0 100644 (file)
@@ -512,8 +512,6 @@ class DummyPurePathTest(unittest.TestCase):
         self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
         self.assertFalse(P('a/b/c.py').full_match('/a/b/c.py/**'))
         self.assertFalse(P('a/b/c.py').full_match('/**/a/b/c.py'))
-        self.assertRaises(ValueError, P('a').full_match, '**a/b/c')
-        self.assertRaises(ValueError, P('a').full_match, 'a/b/c**')
         # Case-sensitive flag
         self.assertFalse(P('A.py').full_match('a.PY', case_sensitive=True))
         self.assertTrue(P('A.py').full_match('a.PY', case_sensitive=False))