[Python-checkins] bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)

Miss Skeleton (bot) webhook-mailer at python.org
Mon Oct 21 14:12:21 EDT 2019


https://github.com/python/cpython/commit/175abccbbfccb2f6489dc5c73f4630c1b25ce504
commit: 175abccbbfccb2f6489dc5c73f4630c1b25ce504
branch: 3.7
author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-10-21T11:12:17-07:00
summary:

bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)

(cherry picked from commit 10ecbadb799ddf3393d1fc80119a3db14724d381)

Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>

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 24437f8f95d13..af9747ba3cb99 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
@@ -444,7 +450,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 == '**':
@@ -455,7 +461,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)
@@ -465,10 +471,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()
@@ -494,9 +500,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:
@@ -510,13 +516,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
@@ -527,8 +532,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
@@ -539,8 +543,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
@@ -1101,11 +1105,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
 
@@ -1114,11 +1117,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 9c82c6451479c..e1d699a0d5246 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -2216,11 +2216,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