mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
1cf0301086
commit
0634201f53
|
@ -136,8 +136,7 @@ The :mod:`glob` module defines the following functions:
|
||||||
separators, and ``*`` pattern segments match precisely one path segment.
|
separators, and ``*`` pattern segments match precisely one path segment.
|
||||||
|
|
||||||
If *recursive* is true, the pattern segment "``**``" will match any number
|
If *recursive* is true, the pattern segment "``**``" will match any number
|
||||||
of path segments. If "``**``" occurs in any position other than a full
|
of path segments.
|
||||||
pattern segment, :exc:`ValueError` is raised.
|
|
||||||
|
|
||||||
If *include_hidden* is true, wildcards can match path segments that start
|
If *include_hidden* is true, wildcards can match path segments that start
|
||||||
with a dot (``.``).
|
with a dot (``.``).
|
||||||
|
|
33
Lib/glob.py
33
Lib/glob.py
|
@ -256,8 +256,7 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
|
||||||
"""Translate a pathname with shell wildcards to a regular expression.
|
"""Translate a pathname with shell wildcards to a regular expression.
|
||||||
|
|
||||||
If `recursive` is true, the pattern segment '**' will match any number of
|
If `recursive` is true, the pattern segment '**' will match any number of
|
||||||
path segments; if '**' appears outside its own segment, ValueError will be
|
path segments.
|
||||||
raised.
|
|
||||||
|
|
||||||
If `include_hidden` is true, wildcards can match path segments beginning
|
If `include_hidden` is true, wildcards can match path segments beginning
|
||||||
with a dot ('.').
|
with a dot ('.').
|
||||||
|
@ -291,22 +290,18 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
|
||||||
for idx, part in enumerate(parts):
|
for idx, part in enumerate(parts):
|
||||||
if part == '*':
|
if part == '*':
|
||||||
results.append(one_segment if idx < last_part_idx else one_last_segment)
|
results.append(one_segment if idx < last_part_idx else one_last_segment)
|
||||||
continue
|
elif recursive and part == '**':
|
||||||
if recursive:
|
if idx < last_part_idx:
|
||||||
if part == '**':
|
if parts[idx + 1] != '**':
|
||||||
if idx < last_part_idx:
|
results.append(any_segments)
|
||||||
if parts[idx + 1] != '**':
|
else:
|
||||||
results.append(any_segments)
|
results.append(any_last_segments)
|
||||||
else:
|
else:
|
||||||
results.append(any_last_segments)
|
if part:
|
||||||
continue
|
if not include_hidden and part[0] in '*?':
|
||||||
elif '**' in part:
|
results.append(r'(?!\.)')
|
||||||
raise ValueError("Invalid pattern: '**' can only be an entire path component")
|
results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
|
||||||
if part:
|
if idx < last_part_idx:
|
||||||
if not include_hidden and part[0] in '*?':
|
results.append(any_sep)
|
||||||
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)
|
res = ''.join(results)
|
||||||
return fr'(?s:{res})\Z'
|
return fr'(?s:{res})\Z'
|
||||||
|
|
|
@ -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.assertEqual(fn('**'), r'(?s:.*)\Z')
|
||||||
self.assertEqual(fn('**/**'), r'(?s:.*)\Z')
|
self.assertEqual(fn('**/**'), r'(?s:.*)\Z')
|
||||||
self.assertRaises(ValueError, fn, '***')
|
self.assertEqual(fn('***'), r'(?s:[^/]*)\Z')
|
||||||
self.assertRaises(ValueError, fn, 'a**')
|
self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z')
|
||||||
self.assertRaises(ValueError, fn, '**b')
|
self.assertEqual(fn('**b'), r'(?s:[^/]*b)\Z')
|
||||||
self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/(?:.+/)?[^/]+/[^/]*\.[^/]*/.*)\Z')
|
self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/(?:.+/)?[^/]+/[^/]*\.[^/]*/.*)\Z')
|
||||||
|
|
||||||
def test_translate_seps(self):
|
def test_translate_seps(self):
|
||||||
|
|
|
@ -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./**'))
|
||||||
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.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
|
# Case-sensitive flag
|
||||||
self.assertFalse(P('A.py').full_match('a.PY', case_sensitive=True))
|
self.assertFalse(P('A.py').full_match('a.PY', case_sensitive=True))
|
||||||
self.assertTrue(P('A.py').full_match('a.PY', case_sensitive=False))
|
self.assertTrue(P('A.py').full_match('a.PY', case_sensitive=False))
|
||||||
|
|
Loading…
Reference in New Issue