diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 79e0b7f09eaa77..602a6da13ee812 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -592,9 +592,8 @@ Pure paths provide the following methods and properties: Return ``True`` if matching is successful, ``False`` otherwise. This method is similar to :meth:`~PurePath.full_match`, but empty patterns - aren't allowed (:exc:`ValueError` is raised), the recursive wildcard - "``**``" isn't supported (it acts like non-recursive "``*``"), and if a - relative pattern is provided, then matching is done from the right:: + aren't allowed (:exc:`ValueError` is raised), and if a relative pattern is + provided, then matching is done from the right:: >>> PurePath('a/b.py').match('*.py') True @@ -609,6 +608,10 @@ Pure paths provide the following methods and properties: .. versionchanged:: 3.12 The *case_sensitive* parameter was added. + .. versionchanged:: next + Added support for the recursive wildcard "``**``". In previous versions, + it acted like non-recursive "``*``". + .. method:: PurePath.relative_to(other, walk_up=False) diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index 8a892102cc00ea..3fdc1c470322dd 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -566,27 +566,20 @@ def match(self, path_pattern, *, case_sensitive=None): """ Return True if this path matches the given pattern. If the pattern is relative, matching is done from the right; otherwise, the entire path - is matched. The recursive wildcard '**' is *not* supported by this - method. + is matched. """ if not hasattr(path_pattern, 'with_segments'): path_pattern = self.with_segments(path_pattern) if case_sensitive is None: case_sensitive = self.parser is posixpath - path_parts = self.parts[::-1] - pattern_parts = path_pattern.parts[::-1] - if not pattern_parts: + if not path_pattern.parts: raise ValueError("empty pattern") - if len(path_parts) < len(pattern_parts): - return False - if len(path_parts) > len(pattern_parts) and path_pattern.anchor: - return False - globber = _StringGlobber(self.parser.sep, case_sensitive) - for path_part, pattern_part in zip(path_parts, pattern_parts): - match = globber.compile(pattern_part) - if match(path_part) is None: - return False - return True + if not path_pattern.anchor: + path_pattern = '**' / path_pattern + path = str(self) if self.parts else '' + pattern = str(path_pattern) + globber = _StringGlobber(self.parser.sep, case_sensitive, recursive=True) + return globber.compile(pattern)(path) is not None # Subclassing os.PathLike makes isinstance() checks slower, # which in turn makes Path construction slower. Register instead! diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index a1105aae6351b6..4df660b0c83e6e 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -584,8 +584,15 @@ def test_match_common(self): self.assertFalse(P('/ab.py').match('/a/*.py')) self.assertFalse(P('/a/b/c.py').match('/a/*.py')) # Multi-part glob-style pattern. - self.assertFalse(P('/a/b/c.py').match('/**/*.py')) + self.assertTrue(P('/a/b/c.py').match('/**/*.py')) self.assertTrue(P('/a/b/c.py').match('/a/**/*.py')) + self.assertTrue(P('/a/b/c.py').match('/a/b/**/*.py')) + self.assertTrue(P('/a/b/c.py').match('/**/**/**/**/*.py')) + self.assertTrue(P('/a/b/c.py').match('/**')) + self.assertTrue(P('/a/b/c.py').match('/a/**')) + self.assertFalse(P('a/b/c.py').match('/a/b/c.py/**')) + self.assertFalse(P('a/b/c.py').match('/**/a/b/c.py')) + self.assertFalse(P('c.py').match('c/**')) # 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)) @@ -593,7 +600,7 @@ def test_match_common(self): self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', case_sensitive=False)) # Matching against empty path self.assertFalse(P('').match('*')) - self.assertFalse(P('').match('**')) + self.assertTrue(P('').match('**')) self.assertFalse(P('').match('**/*')) @needs_posix diff --git a/Misc/NEWS.d/next/Library/2025-09-24-07-26-29.gh-issue-139282.UTkFqE.rst b/Misc/NEWS.d/next/Library/2025-09-24-07-26-29.gh-issue-139282.UTkFqE.rst new file mode 100644 index 00000000000000..5e6c4ec2a3ee47 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-24-07-26-29.gh-issue-139282.UTkFqE.rst @@ -0,0 +1,2 @@ +Add support for the recursive wildcard "``**``" in +:meth:`pathlib.PurePath.match`.