Skip to content

Commit 0634201

Browse files
authored
GH-116377: Stop raising ValueError from glob.translate(). (#116378)
Stop raising `ValueError` from `glob.translate()` when a `**` sub-string appears in a non-recursive pattern segment. This matches `glob.glob()` behaviour.
1 parent 1cf0301 commit 0634201

File tree

4 files changed

+18
-26
lines changed

4 files changed

+18
-26
lines changed

Doc/library/glob.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,7 @@ The :mod:`glob` module defines the following functions:
136136
separators, and ``*`` pattern segments match precisely one path segment.
137137

138138
If *recursive* is true, the pattern segment "``**``" will match any number
139-
of path segments. If "``**``" occurs in any position other than a full
140-
pattern segment, :exc:`ValueError` is raised.
139+
of path segments.
141140

142141
If *include_hidden* is true, wildcards can match path segments that start
143142
with a dot (``.``).

Lib/glob.py

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,7 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
256256
"""Translate a pathname with shell wildcards to a regular expression.
257257
258258
If `recursive` is true, the pattern segment '**' will match any number of
259-
path segments; if '**' appears outside its own segment, ValueError will be
260-
raised.
259+
path segments.
261260
262261
If `include_hidden` is true, wildcards can match path segments beginning
263262
with a dot ('.').
@@ -291,22 +290,18 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
291290
for idx, part in enumerate(parts):
292291
if part == '*':
293292
results.append(one_segment if idx < last_part_idx else one_last_segment)
294-
continue
295-
if recursive:
296-
if part == '**':
297-
if idx < last_part_idx:
298-
if parts[idx + 1] != '**':
299-
results.append(any_segments)
300-
else:
301-
results.append(any_last_segments)
302-
continue
303-
elif '**' in part:
304-
raise ValueError("Invalid pattern: '**' can only be an entire path component")
305-
if part:
306-
if not include_hidden and part[0] in '*?':
307-
results.append(r'(?!\.)')
308-
results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
309-
if idx < last_part_idx:
310-
results.append(any_sep)
293+
elif recursive and part == '**':
294+
if idx < last_part_idx:
295+
if parts[idx + 1] != '**':
296+
results.append(any_segments)
297+
else:
298+
results.append(any_last_segments)
299+
else:
300+
if part:
301+
if not include_hidden and part[0] in '*?':
302+
results.append(r'(?!\.)')
303+
results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
304+
if idx < last_part_idx:
305+
results.append(any_sep)
311306
res = ''.join(results)
312307
return fr'(?s:{res})\Z'

Lib/test/test_glob.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,9 @@ def fn(pat):
452452
self.assertEqual(fn('?'), r'(?s:[^/])\Z')
453453
self.assertEqual(fn('**'), r'(?s:.*)\Z')
454454
self.assertEqual(fn('**/**'), r'(?s:.*)\Z')
455-
self.assertRaises(ValueError, fn, '***')
456-
self.assertRaises(ValueError, fn, 'a**')
457-
self.assertRaises(ValueError, fn, '**b')
455+
self.assertEqual(fn('***'), r'(?s:[^/]*)\Z')
456+
self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z')
457+
self.assertEqual(fn('**b'), r'(?s:[^/]*b)\Z')
458458
self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/(?:.+/)?[^/]+/[^/]*\.[^/]*/.*)\Z')
459459

460460
def test_translate_seps(self):

Lib/test/test_pathlib/test_pathlib_abc.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,6 @@ def test_full_match_common(self):
512512
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
513513
self.assertFalse(P('a/b/c.py').full_match('/a/b/c.py/**'))
514514
self.assertFalse(P('a/b/c.py').full_match('/**/a/b/c.py'))
515-
self.assertRaises(ValueError, P('a').full_match, '**a/b/c')
516-
self.assertRaises(ValueError, P('a').full_match, 'a/b/c**')
517515
# Case-sensitive flag
518516
self.assertFalse(P('A.py').full_match('a.PY', case_sensitive=True))
519517
self.assertTrue(P('A.py').full_match('a.PY', case_sensitive=False))

0 commit comments

Comments
 (0)