]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-70303: Make `pathlib.Path.glob('**')` return both files and directories (#114684)
authorBarney Gale <barney.gale@gmail.com>
Tue, 30 Jan 2024 19:52:53 +0000 (19:52 +0000)
committerGitHub <noreply@github.com>
Tue, 30 Jan 2024 19:52:53 +0000 (19:52 +0000)
Return files and directories from `pathlib.Path.glob()` if the pattern ends
with `**`. This is more compatible with `PurePath.full_match()` and with
other glob implementations such as bash and `glob.glob()`. Users can add a
trailing slash to match only directories.

In my previous patch I added a `FutureWarning` with the intention of fixing
this in Python 3.15. Upon further reflection I think this was an
unnecessarily cautious remedy to a clear bug.

Doc/library/pathlib.rst
Doc/whatsnew/3.13.rst
Lib/pathlib/__init__.py
Lib/test/test_pathlib/test_pathlib.py
Lib/test/test_pathlib/test_pathlib_abc.py
Misc/NEWS.d/next/Library/2024-01-28-18-38-18.gh-issue-70303._Lt_pj.rst [new file with mode: 0644]

index f1aba793fda03e7b7dd9e2700ae4d101c893ed26..f94b6fb38056847f75597cef98bb9feb8d71d5c9 100644 (file)
@@ -1038,9 +1038,8 @@ call fails (for example because the path doesn't exist).
       The *follow_symlinks* parameter was added.
 
    .. versionchanged:: 3.13
-      Emits :exc:`FutureWarning` if the pattern ends with "``**``". In a
-      future Python release, patterns with this ending will match both files
-      and directories. Add a trailing slash to match only directories.
+      Return files and directories if *pattern* ends with "``**``". In
+      previous versions, only directories were returned.
 
    .. versionchanged:: 3.13
       The *pattern* parameter accepts a :term:`path-like object`.
index fec1e55e0daf0e6ad3589f9d286a8fd40e1df0c5..b33203efbb05c0c64ebe7ab31c314ac0f6c23747 100644 (file)
@@ -350,6 +350,11 @@ pathlib
   (Contributed by Barney Gale in :gh:`77609` and :gh:`105793`, and
   Kamil Turek in :gh:`107962`).
 
+* Return files and directories from :meth:`pathlib.Path.glob` and
+  :meth:`~pathlib.Path.rglob` when given a pattern that ends with "``**``". In
+  earlier versions, only directories were returned.
+  (Contributed by Barney Gale in :gh:`70303`).
+
 pdb
 ---
 
@@ -1211,6 +1216,11 @@ Changes in the Python API
 * :class:`mailbox.Maildir` now ignores files with a leading dot.
   (Contributed by Zackery Spytz in :gh:`65559`.)
 
+* :meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` now return both
+  files and directories if a pattern that ends with "``**``" is given, rather
+  than directories only. Users may add a trailing slash to match only
+  directories.
+
 
 Build Changes
 =============
index cc159edab5796f54fad9aa0663bc4958139a3f9b..4447f98e3e86897f43eed8c3b226b2f17358bc46 100644 (file)
@@ -465,14 +465,6 @@ class PurePath(_abc.PurePathBase):
         elif pattern[-1] in (self.pathmod.sep, self.pathmod.altsep):
             # GH-65238: pathlib doesn't preserve trailing slash. Add it back.
             parts.append('')
-        elif parts[-1] == '**':
-            # GH-70303: '**' only matches directories. Add trailing slash.
-            warnings.warn(
-                "Pattern ending '**' will match files and directories in a "
-                "future Python release. Add a trailing slash to match only "
-                "directories and remove this warning.",
-                FutureWarning, 4)
-            parts.append('')
         parts.reverse()
         return parts
 
index a8cc30ef0ab63f684e834c6c5e9850163baabc8d..3bee9b8c762b805a6ca4f9901ffc720415bbcc54 100644 (file)
@@ -1258,18 +1258,6 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
         with set_recursion_limit(recursion_limit):
             list(base.glob('**/'))
 
-    def test_glob_recursive_no_trailing_slash(self):
-        P = self.cls
-        p = P(self.base)
-        with self.assertWarns(FutureWarning):
-            p.glob('**')
-        with self.assertWarns(FutureWarning):
-            p.glob('*/**')
-        with self.assertWarns(FutureWarning):
-            p.rglob('**')
-        with self.assertWarns(FutureWarning):
-            p.rglob('*/**')
-
     def test_glob_pathlike(self):
         P = self.cls
         p = P(self.base)
index 18a612f6b81bacea79e3e650cfd552124844139e..0e12182c162c14218ea77e11c4b234641e59fee8 100644 (file)
@@ -1765,16 +1765,26 @@ class DummyPathTest(DummyPurePathTest):
         _check(p, "*/fileB", ["dirB/fileB", "linkB/fileB"])
         _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"])
         _check(p, "dir*/*/..", ["dirC/dirD/..", "dirA/linkC/..", "dirB/linkD/.."])
+        _check(p, "dir*/**", [
+            "dirA", "dirA/linkC", "dirA/linkC/fileB", "dirA/linkC/linkD", "dirA/linkC/linkD/fileB",
+            "dirB", "dirB/fileB", "dirB/linkD", "dirB/linkD/fileB",
+            "dirC", "dirC/fileC", "dirC/dirD",  "dirC/dirD/fileD", "dirC/novel.txt",
+            "dirE"])
         _check(p, "dir*/**/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
                                "dirC/", "dirC/dirD/", "dirE/"])
         _check(p, "dir*/**/..", ["dirA/..", "dirA/linkC/..", "dirB/..",
                                  "dirB/linkD/..", "dirA/linkC/linkD/..",
                                  "dirC/..", "dirC/dirD/..", "dirE/.."])
+        _check(p, "dir*/*/**", [
+            "dirA/linkC", "dirA/linkC/linkD", "dirA/linkC/fileB", "dirA/linkC/linkD/fileB",
+            "dirB/linkD", "dirB/linkD/fileB",
+            "dirC/dirD", "dirC/dirD/fileD"])
         _check(p, "dir*/*/**/", ["dirA/linkC/", "dirA/linkC/linkD/", "dirB/linkD/", "dirC/dirD/"])
         _check(p, "dir*/*/**/..", ["dirA/linkC/..", "dirA/linkC/linkD/..",
                                    "dirB/linkD/..", "dirC/dirD/.."])
         _check(p, "dir*/**/fileC", ["dirC/fileC"])
         _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"])
+        _check(p, "*/dirD/**", ["dirC/dirD", "dirC/dirD/fileD"])
         _check(p, "*/dirD/**/", ["dirC/dirD/"])
 
     @needs_symlinks
@@ -1791,12 +1801,20 @@ class DummyPathTest(DummyPurePathTest):
         _check(p, "*/fileB", ["dirB/fileB"])
         _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/"])
         _check(p, "dir*/*/..", ["dirC/dirD/.."])
+        _check(p, "dir*/**", [
+            "dirA", "dirA/linkC",
+            "dirB", "dirB/fileB", "dirB/linkD",
+            "dirC", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt",
+            "dirE"])
         _check(p, "dir*/**/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"])
         _check(p, "dir*/**/..", ["dirA/..", "dirB/..", "dirC/..", "dirC/dirD/..", "dirE/.."])
+        _check(p, "dir*/*/**", ["dirC/dirD", "dirC/dirD/fileD"])
         _check(p, "dir*/*/**/", ["dirC/dirD/"])
         _check(p, "dir*/*/**/..", ["dirC/dirD/.."])
         _check(p, "dir*/**/fileC", ["dirC/fileC"])
+        _check(p, "dir*/*/../dirD/**", ["dirC/dirD/../dirD", "dirC/dirD/../dirD/fileD"])
         _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"])
+        _check(p, "*/dirD/**", ["dirC/dirD", "dirC/dirD/fileD"])
         _check(p, "*/dirD/**/", ["dirC/dirD/"])
 
     def test_rglob_common(self):
@@ -1833,10 +1851,13 @@ class DummyPathTest(DummyPurePathTest):
                               "dirC/dirD", "dirC/dirD/fileD"])
         _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
         _check(p.rglob("**/file*"), ["dirC/fileC", "dirC/dirD/fileD"])
+        _check(p.rglob("dir*/**"), ["dirC/dirD", "dirC/dirD/fileD"])
         _check(p.rglob("dir*/**/"), ["dirC/dirD/"])
         _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
         _check(p.rglob("*/"), ["dirC/dirD/"])
         _check(p.rglob(""), ["dirC/", "dirC/dirD/"])
+        _check(p.rglob("**"), [
+            "dirC", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt"])
         _check(p.rglob("**/"), ["dirC/", "dirC/dirD/"])
         # gh-91616, a re module regression
         _check(p.rglob("*.txt"), ["dirC/novel.txt"])
diff --git a/Misc/NEWS.d/next/Library/2024-01-28-18-38-18.gh-issue-70303._Lt_pj.rst b/Misc/NEWS.d/next/Library/2024-01-28-18-38-18.gh-issue-70303._Lt_pj.rst
new file mode 100644 (file)
index 0000000..dedda24
--- /dev/null
@@ -0,0 +1,2 @@
+Return both files and directories from :meth:`pathlib.Path.glob` if a
+pattern ends with "``**``". Previously only directories were returned.