[Python-checkins] bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)
Serhiy Storchaka
webhook-mailer at python.org
Mon Oct 21 13:37:23 EDT 2019
https://github.com/python/cpython/commit/10ecbadb799ddf3393d1fc80119a3db14724d381
commit: 10ecbadb799ddf3393d1fc80119a3db14724d381
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-10-21T20:37:15+03:00
summary:
bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)
files:
A Misc/NEWS.d/next/Library/2019-10-20-12-04-48.bpo-31202.NfdIus.rst
M Lib/pathlib.py
M Lib/test/test_pathlib.py
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index 7bd66c10cbdb6..67b94e0154541 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -187,6 +187,9 @@ def casefold(self, s):
def casefold_parts(self, 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):
s = str(path)
if not s:
@@ -309,6 +312,9 @@ def casefold(self, s):
def casefold_parts(self, parts):
return parts
+ def compile_pattern(self, pattern):
+ return re.compile(fnmatch.translate(pattern)).fullmatch
+
def resolve(self, path, strict=False):
sep = self.sep
accessor = path._accessor
@@ -446,7 +452,7 @@ def readlink(self, path):
# Globbing helpers
#
-def _make_selector(pattern_parts):
+def _make_selector(pattern_parts, flavour):
pat = pattern_parts[0]
child_parts = pattern_parts[1:]
if pat == '**':
@@ -457,7 +463,7 @@ def _make_selector(pattern_parts):
cls = _WildcardSelector
else:
cls = _PreciseSelector
- return cls(pat, child_parts)
+ return cls(pat, child_parts, flavour)
if hasattr(functools, "lru_cache"):
_make_selector = functools.lru_cache()(_make_selector)
@@ -467,10 +473,10 @@ class _Selector:
"""A selector matches a specific glob pattern part against the children
of a given path."""
- def __init__(self, child_parts):
+ def __init__(self, child_parts, flavour):
self.child_parts = child_parts
if child_parts:
- self.successor = _make_selector(child_parts)
+ self.successor = _make_selector(child_parts, flavour)
self.dironly = True
else:
self.successor = _TerminatingSelector()
@@ -496,9 +502,9 @@ def _select_from(self, parent_path, is_dir, exists, scandir):
class _PreciseSelector(_Selector):
- def __init__(self, name, child_parts):
+ def __init__(self, name, child_parts, flavour):
self.name = name
- _Selector.__init__(self, child_parts)
+ _Selector.__init__(self, child_parts, flavour)
def _select_from(self, parent_path, is_dir, exists, scandir):
try:
@@ -512,13 +518,12 @@ def _select_from(self, parent_path, is_dir, exists, scandir):
class _WildcardSelector(_Selector):
- def __init__(self, pat, child_parts):
- self.pat = re.compile(fnmatch.translate(pat))
- _Selector.__init__(self, child_parts)
+ def __init__(self, pat, child_parts, flavour):
+ self.match = flavour.compile_pattern(pat)
+ _Selector.__init__(self, child_parts, flavour)
def _select_from(self, parent_path, is_dir, exists, scandir):
try:
- cf = parent_path._flavour.casefold
entries = list(scandir(parent_path))
for entry in entries:
entry_is_dir = False
@@ -529,8 +534,7 @@ def _select_from(self, parent_path, is_dir, exists, scandir):
raise
if not self.dironly or entry_is_dir:
name = entry.name
- casefolded = cf(name)
- if self.pat.match(casefolded):
+ if self.match(name):
path = parent_path._make_child_relpath(name)
for p in self.successor._select_from(path, is_dir, exists, scandir):
yield p
@@ -541,8 +545,8 @@ def _select_from(self, parent_path, is_dir, exists, scandir):
class _RecursiveWildcardSelector(_Selector):
- def __init__(self, pat, child_parts):
- _Selector.__init__(self, child_parts)
+ def __init__(self, pat, child_parts, flavour):
+ _Selector.__init__(self, child_parts, flavour)
def _iterate_directories(self, parent_path, is_dir, scandir):
yield parent_path
@@ -1118,11 +1122,10 @@ def glob(self, pattern):
"""
if not pattern:
raise ValueError("Unacceptable pattern: {!r}".format(pattern))
- pattern = self._flavour.casefold(pattern)
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
if drv or root:
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):
yield p
@@ -1131,11 +1134,10 @@ def rglob(self, pattern):
directories) matching the given relative pattern, anywhere in
this subtree.
"""
- pattern = self._flavour.casefold(pattern)
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
if drv or root:
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):
yield p
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index f3304f01b4bee..221c2722d0a9d 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -2378,11 +2378,15 @@ def test_glob(self):
P = self.cls
p = P(BASE)
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):
P = self.cls
p = P(BASE, "dirC")
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):
P = self.cls
diff --git a/Misc/NEWS.d/next/Library/2019-10-20-12-04-48.bpo-31202.NfdIus.rst b/Misc/NEWS.d/next/Library/2019-10-20-12-04-48.bpo-31202.NfdIus.rst
new file mode 100644
index 0000000000000..8edb09d61317b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-10-20-12-04-48.bpo-31202.NfdIus.rst
@@ -0,0 +1,2 @@
+The case the result of :func:`pathlib.WindowsPath.glob` matches now the case
+of the pattern for literal parts.
More information about the Python-checkins
mailing list