]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-104484: Add case_sensitive argument to `pathlib.PurePath.match()` (GH-104565)
authorthirumurugan <67573527+thirumurugan-git@users.noreply.github.com>
Thu, 18 May 2023 17:59:31 +0000 (23:29 +0530)
committerGitHub <noreply@github.com>
Thu, 18 May 2023 17:59:31 +0000 (18:59 +0100)
Co-authored-by: Barney Gale <barney.gale@gmail.com>
Doc/library/pathlib.rst
Doc/whatsnew/3.12.rst
Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS.d/next/Library/2023-05-17-03-14-07.gh-issue-104484.y6KxL6.rst [new file with mode: 0644]

index 93af07ae5ac10f1708237d9fbd2d7e4f1f9d312d..627f2df9263dec99062d9292b4dfd855a0df773b 100644 (file)
@@ -546,7 +546,7 @@ Pure paths provide the following methods and properties:
       PureWindowsPath('c:/Program Files')
 
 
-.. method:: PurePath.match(pattern)
+.. method:: PurePath.match(pattern, *, case_sensitive=None)
 
    Match this path against the provided glob-style pattern.  Return ``True``
    if matching is successful, ``False`` otherwise.
@@ -576,6 +576,11 @@ Pure paths provide the following methods and properties:
       >>> PureWindowsPath('b.py').match('*.PY')
       True
 
+   Set *case_sensitive* to ``True`` or ``False`` to override this behaviour.
+
+   .. versionadded:: 3.12
+      The *case_sensitive* argument.
+
 
 .. method:: PurePath.relative_to(other, walk_up=False)
 
index 3e55b3fa0f4734f1f4916d9b535f26df0e6a404a..25f0a4c3ca26dabe0d13df8442952173471545ce 100644 (file)
@@ -395,6 +395,9 @@ pathlib
 * Add :meth:`pathlib.Path.is_junction` as a proxy to :func:`os.path.isjunction`.
   (Contributed by Charles Machalow in :gh:`99547`.)
 
+* Add *case_sensitive* optional parameter to :meth:`pathlib.Path.glob`,
+  :meth:`pathlib.Path.rglob` and :meth:`pathlib.PurePath.match` for matching
+  the path's case sensitivity, allowing for more precise control over the matching process.
 
 dis
 ---
index ef7c47c9e775e4e7d87519c1e76989d2c5ad7072..3d68c161603d0876d8e9a9c8c0665b9365960d5b 100644 (file)
@@ -86,6 +86,12 @@ def _make_selector(pattern_parts, flavour, case_sensitive):
     return cls(pat, child_parts, flavour, case_sensitive)
 
 
+@functools.lru_cache(maxsize=256)
+def _compile_pattern(pat, case_sensitive):
+    flags = re.NOFLAG if case_sensitive else re.IGNORECASE
+    return re.compile(fnmatch.translate(pat), flags).match
+
+
 class _Selector:
     """A selector matches a specific glob pattern part against the children
     of a given path."""
@@ -133,8 +139,7 @@ class _WildcardSelector(_Selector):
         if case_sensitive is None:
             # TODO: evaluate case-sensitivity of each directory in _select_from()
             case_sensitive = _is_case_sensitive(flavour)
-        flags = re.NOFLAG if case_sensitive else re.IGNORECASE
-        self.match = re.compile(fnmatch.translate(pat), flags=flags).fullmatch
+        self.match = _compile_pattern(pat, case_sensitive)
 
     def _select_from(self, parent_path, scandir):
         try:
@@ -680,22 +685,25 @@ class PurePath(object):
         name = self._tail[-1].partition('.')[0].partition(':')[0].rstrip(' ')
         return name.upper() in _WIN_RESERVED_NAMES
 
-    def match(self, path_pattern):
+    def match(self, path_pattern, *, case_sensitive=None):
         """
         Return True if this path matches the given pattern.
         """
+        if case_sensitive is None:
+            case_sensitive = _is_case_sensitive(self._flavour)
         pat = self.with_segments(path_pattern)
         if not pat.parts:
             raise ValueError("empty pattern")
-        pat_parts = pat._parts_normcase
-        parts = self._parts_normcase
+        pat_parts = pat.parts
+        parts = self.parts
         if pat.drive or pat.root:
             if len(pat_parts) != len(parts):
                 return False
         elif len(pat_parts) > len(parts):
             return False
         for part, pat in zip(reversed(parts), reversed(pat_parts)):
-            if not fnmatch.fnmatchcase(part, pat):
+            match = _compile_pattern(pat, case_sensitive)
+            if not match(part):
                 return False
         return True
 
index 46a5248499c5d0b384cb01505c798cb276483068..ab2c2b232a04115fc68d8267019e9491b6a103c2 100644 (file)
@@ -312,6 +312,11 @@ class _BasePurePathTest(object):
         # Multi-part glob-style pattern.
         self.assertFalse(P('/a/b/c.py').match('/**/*.py'))
         self.assertTrue(P('/a/b/c.py').match('/a/**/*.py'))
+        # Case-sensitive flag
+        self.assertFalse(P('A.py').match('a.PY', case_sensitive=True))
+        self.assertTrue(P('A.py').match('a.PY', case_sensitive=False))
+        self.assertFalse(P('c:/a/B.Py').match('C:/A/*.pY', case_sensitive=True))
+        self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', case_sensitive=False))
 
     def test_ordering_common(self):
         # Ordering is tuple-alike.
@@ -916,7 +921,7 @@ class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
         self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
                          'file://some/share/a/b%25%23c%C3%A9')
 
-    def test_match_common(self):
+    def test_match(self):
         P = self.cls
         # Absolute patterns.
         self.assertTrue(P('c:/b.py').match('*:/*.py'))
diff --git a/Misc/NEWS.d/next/Library/2023-05-17-03-14-07.gh-issue-104484.y6KxL6.rst b/Misc/NEWS.d/next/Library/2023-05-17-03-14-07.gh-issue-104484.y6KxL6.rst
new file mode 100644 (file)
index 0000000..6d42078
--- /dev/null
@@ -0,0 +1 @@
+Added *case_sensitive* argument to :meth:`pathlib.PurePath.match`