bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)
This commit is contained in:
parent
1e73945470
commit
10ecbadb79
|
@ -187,6 +187,9 @@ class _WindowsFlavour(_Flavour):
|
||||||
def casefold_parts(self, parts):
|
def casefold_parts(self, parts):
|
||||||
return [p.lower() for p in parts]
|
return [p.lower() for p in parts]
|
||||||
|
|
||||||
|
def compile_pattern(self, pattern):
|
||||||
|
return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch
|
||||||
|
|
||||||
def resolve(self, path, strict=False):
|
def resolve(self, path, strict=False):
|
||||||
s = str(path)
|
s = str(path)
|
||||||
if not s:
|
if not s:
|
||||||
|
@ -309,6 +312,9 @@ class _PosixFlavour(_Flavour):
|
||||||
def casefold_parts(self, parts):
|
def casefold_parts(self, parts):
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
|
def compile_pattern(self, pattern):
|
||||||
|
return re.compile(fnmatch.translate(pattern)).fullmatch
|
||||||
|
|
||||||
def resolve(self, path, strict=False):
|
def resolve(self, path, strict=False):
|
||||||
sep = self.sep
|
sep = self.sep
|
||||||
accessor = path._accessor
|
accessor = path._accessor
|
||||||
|
@ -446,7 +452,7 @@ _normal_accessor = _NormalAccessor()
|
||||||
# Globbing helpers
|
# Globbing helpers
|
||||||
#
|
#
|
||||||
|
|
||||||
def _make_selector(pattern_parts):
|
def _make_selector(pattern_parts, flavour):
|
||||||
pat = pattern_parts[0]
|
pat = pattern_parts[0]
|
||||||
child_parts = pattern_parts[1:]
|
child_parts = pattern_parts[1:]
|
||||||
if pat == '**':
|
if pat == '**':
|
||||||
|
@ -457,7 +463,7 @@ def _make_selector(pattern_parts):
|
||||||
cls = _WildcardSelector
|
cls = _WildcardSelector
|
||||||
else:
|
else:
|
||||||
cls = _PreciseSelector
|
cls = _PreciseSelector
|
||||||
return cls(pat, child_parts)
|
return cls(pat, child_parts, flavour)
|
||||||
|
|
||||||
if hasattr(functools, "lru_cache"):
|
if hasattr(functools, "lru_cache"):
|
||||||
_make_selector = functools.lru_cache()(_make_selector)
|
_make_selector = functools.lru_cache()(_make_selector)
|
||||||
|
@ -467,10 +473,10 @@ class _Selector:
|
||||||
"""A selector matches a specific glob pattern part against the children
|
"""A selector matches a specific glob pattern part against the children
|
||||||
of a given path."""
|
of a given path."""
|
||||||
|
|
||||||
def __init__(self, child_parts):
|
def __init__(self, child_parts, flavour):
|
||||||
self.child_parts = child_parts
|
self.child_parts = child_parts
|
||||||
if child_parts:
|
if child_parts:
|
||||||
self.successor = _make_selector(child_parts)
|
self.successor = _make_selector(child_parts, flavour)
|
||||||
self.dironly = True
|
self.dironly = True
|
||||||
else:
|
else:
|
||||||
self.successor = _TerminatingSelector()
|
self.successor = _TerminatingSelector()
|
||||||
|
@ -496,9 +502,9 @@ class _TerminatingSelector:
|
||||||
|
|
||||||
class _PreciseSelector(_Selector):
|
class _PreciseSelector(_Selector):
|
||||||
|
|
||||||
def __init__(self, name, child_parts):
|
def __init__(self, name, child_parts, flavour):
|
||||||
self.name = name
|
self.name = name
|
||||||
_Selector.__init__(self, child_parts)
|
_Selector.__init__(self, child_parts, flavour)
|
||||||
|
|
||||||
def _select_from(self, parent_path, is_dir, exists, scandir):
|
def _select_from(self, parent_path, is_dir, exists, scandir):
|
||||||
try:
|
try:
|
||||||
|
@ -512,13 +518,12 @@ class _PreciseSelector(_Selector):
|
||||||
|
|
||||||
class _WildcardSelector(_Selector):
|
class _WildcardSelector(_Selector):
|
||||||
|
|
||||||
def __init__(self, pat, child_parts):
|
def __init__(self, pat, child_parts, flavour):
|
||||||
self.pat = re.compile(fnmatch.translate(pat))
|
self.match = flavour.compile_pattern(pat)
|
||||||
_Selector.__init__(self, child_parts)
|
_Selector.__init__(self, child_parts, flavour)
|
||||||
|
|
||||||
def _select_from(self, parent_path, is_dir, exists, scandir):
|
def _select_from(self, parent_path, is_dir, exists, scandir):
|
||||||
try:
|
try:
|
||||||
cf = parent_path._flavour.casefold
|
|
||||||
entries = list(scandir(parent_path))
|
entries = list(scandir(parent_path))
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
entry_is_dir = False
|
entry_is_dir = False
|
||||||
|
@ -529,8 +534,7 @@ class _WildcardSelector(_Selector):
|
||||||
raise
|
raise
|
||||||
if not self.dironly or entry_is_dir:
|
if not self.dironly or entry_is_dir:
|
||||||
name = entry.name
|
name = entry.name
|
||||||
casefolded = cf(name)
|
if self.match(name):
|
||||||
if self.pat.match(casefolded):
|
|
||||||
path = parent_path._make_child_relpath(name)
|
path = parent_path._make_child_relpath(name)
|
||||||
for p in self.successor._select_from(path, is_dir, exists, scandir):
|
for p in self.successor._select_from(path, is_dir, exists, scandir):
|
||||||
yield p
|
yield p
|
||||||
|
@ -541,8 +545,8 @@ class _WildcardSelector(_Selector):
|
||||||
|
|
||||||
class _RecursiveWildcardSelector(_Selector):
|
class _RecursiveWildcardSelector(_Selector):
|
||||||
|
|
||||||
def __init__(self, pat, child_parts):
|
def __init__(self, pat, child_parts, flavour):
|
||||||
_Selector.__init__(self, child_parts)
|
_Selector.__init__(self, child_parts, flavour)
|
||||||
|
|
||||||
def _iterate_directories(self, parent_path, is_dir, scandir):
|
def _iterate_directories(self, parent_path, is_dir, scandir):
|
||||||
yield parent_path
|
yield parent_path
|
||||||
|
@ -1118,11 +1122,10 @@ class Path(PurePath):
|
||||||
"""
|
"""
|
||||||
if not pattern:
|
if not pattern:
|
||||||
raise ValueError("Unacceptable pattern: {!r}".format(pattern))
|
raise ValueError("Unacceptable pattern: {!r}".format(pattern))
|
||||||
pattern = self._flavour.casefold(pattern)
|
|
||||||
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
|
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
|
||||||
if drv or root:
|
if drv or root:
|
||||||
raise NotImplementedError("Non-relative patterns are unsupported")
|
raise NotImplementedError("Non-relative patterns are unsupported")
|
||||||
selector = _make_selector(tuple(pattern_parts))
|
selector = _make_selector(tuple(pattern_parts), self._flavour)
|
||||||
for p in selector.select_from(self):
|
for p in selector.select_from(self):
|
||||||
yield p
|
yield p
|
||||||
|
|
||||||
|
@ -1131,11 +1134,10 @@ class Path(PurePath):
|
||||||
directories) matching the given relative pattern, anywhere in
|
directories) matching the given relative pattern, anywhere in
|
||||||
this subtree.
|
this subtree.
|
||||||
"""
|
"""
|
||||||
pattern = self._flavour.casefold(pattern)
|
|
||||||
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
|
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
|
||||||
if drv or root:
|
if drv or root:
|
||||||
raise NotImplementedError("Non-relative patterns are unsupported")
|
raise NotImplementedError("Non-relative patterns are unsupported")
|
||||||
selector = _make_selector(("**",) + tuple(pattern_parts))
|
selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
|
||||||
for p in selector.select_from(self):
|
for p in selector.select_from(self):
|
||||||
yield p
|
yield p
|
||||||
|
|
||||||
|
|
|
@ -2378,11 +2378,15 @@ class WindowsPathTest(_BasePathTest, unittest.TestCase):
|
||||||
P = self.cls
|
P = self.cls
|
||||||
p = P(BASE)
|
p = P(BASE)
|
||||||
self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
|
self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
|
||||||
|
self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") })
|
||||||
|
self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"})
|
||||||
|
self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"})
|
||||||
|
|
||||||
def test_rglob(self):
|
def test_rglob(self):
|
||||||
P = self.cls
|
P = self.cls
|
||||||
p = P(BASE, "dirC")
|
p = P(BASE, "dirC")
|
||||||
self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
|
self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
|
||||||
|
self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"})
|
||||||
|
|
||||||
def test_expanduser(self):
|
def test_expanduser(self):
|
||||||
P = self.cls
|
P = self.cls
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
The case the result of :func:`pathlib.WindowsPath.glob` matches now the case
|
||||||
|
of the pattern for literal parts.
|
Loading…
Reference in New Issue