8000 GH-70303: Make `pathlib.Path.glob('**')` return both files and directories by barneygale · Pull Request #114684 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

GH-70303: Make pathlib.Path.glob('**') return both files and directories #114684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
10 changes: 10 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
---

Expand Down Expand Up @@ -1213,6 +1218,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
=============
Expand Down
8 changes: 0 additions & 8 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,14 +465,6 @@ def _pattern_stack(self):
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

Expand Down
12 changes: 0 additions & 12 deletions Lib/test/test_pathlib/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1265,18 +1265,6 @@ def test_glob_above_recursion_limit(self):
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)
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_pathlib/test_pathlib_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1766,16 +1766,26 @@ def _check(path, glob, expected):
_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
Expand All @@ -1792,12 +1802,20 @@ def _check(path, glob, expected):
_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):
Expand Down Expand Up @@ -1834,10 +1852,13 @@ def _check(glob, expected):
"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"])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Return both files and directories from :meth:`pathlib.Path.glob` if a
pattern ends with "``**``". Previously only directories were returned.
0