GH-106747: Make pathlib ABC globbing more consistent with glob.glob() (#115056)

When expanding `**` wildcards, ensure we add a trailing slash to the
topmost directory path. This matches `glob.glob()` behaviour:

    >>> glob.glob('dirA/**', recursive=True)
    ['dirA/', 'dirA/dirB', 'dirA/dirB/dirC']

This does not affect `pathlib.Path.glob()`, because trailing slashes aren't
supported in pathlib proper.
This commit is contained in:
Barney Gale 2024-02-06 02:48:18 +00:00 committed by GitHub
parent 299e16ca0f
commit 1b1f8398d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 18 deletions

View file

@ -95,7 +95,7 @@ def _select_recursive(parent_paths, dir_only, follow_symlinks):
if follow_symlinks is None: if follow_symlinks is None:
follow_symlinks = False follow_symlinks = False
for parent_path in parent_paths: for parent_path in parent_paths:
paths = [parent_path] paths = [parent_path._make_child_relpath('')]
while paths: while paths:
path = paths.pop() path = paths.pop()
yield path yield path

View file

@ -1791,25 +1791,25 @@ class DummyPathTest(DummyPurePathTest):
_check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"]) _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"])
_check(p, "dir*/*/..", ["dirC/dirD/..", "dirA/linkC/..", "dirB/linkD/.."]) _check(p, "dir*/*/..", ["dirC/dirD/..", "dirA/linkC/..", "dirB/linkD/.."])
_check(p, "dir*/**", [ _check(p, "dir*/**", [
"dirA", "dirA/linkC", "dirA/linkC/fileB", "dirA/linkC/linkD", "dirA/linkC/linkD/fileB", "dirA/", "dirA/linkC", "dirA/linkC/fileB", "dirA/linkC/linkD", "dirA/linkC/linkD/fileB",
"dirB", "dirB/fileB", "dirB/linkD", "dirB/linkD/fileB", "dirB/", "dirB/fileB", "dirB/linkD", "dirB/linkD/fileB",
"dirC", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt", "dirC/", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt",
"dirE"]) "dirE/"])
_check(p, "dir*/**/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/", _check(p, "dir*/**/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
"dirC/", "dirC/dirD/", "dirE/"]) "dirC/", "dirC/dirD/", "dirE/"])
_check(p, "dir*/**/..", ["dirA/..", "dirA/linkC/..", "dirB/..", _check(p, "dir*/**/..", ["dirA/..", "dirA/linkC/..", "dirB/..",
"dirB/linkD/..", "dirA/linkC/linkD/..", "dirB/linkD/..", "dirA/linkC/linkD/..",
"dirC/..", "dirC/dirD/..", "dirE/.."]) "dirC/..", "dirC/dirD/..", "dirE/.."])
_check(p, "dir*/*/**", [ _check(p, "dir*/*/**", [
"dirA/linkC", "dirA/linkC/linkD", "dirA/linkC/fileB", "dirA/linkC/linkD/fileB", "dirA/linkC/", "dirA/linkC/linkD", "dirA/linkC/fileB", "dirA/linkC/linkD/fileB",
"dirB/linkD", "dirB/linkD/fileB", "dirB/linkD/", "dirB/linkD/fileB",
"dirC/dirD", "dirC/dirD/fileD"]) "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*/*/**/..", ["dirA/linkC/..", "dirA/linkC/linkD/..", _check(p, "dir*/*/**/..", ["dirA/linkC/..", "dirA/linkC/linkD/..",
"dirB/linkD/..", "dirC/dirD/.."]) "dirB/linkD/..", "dirC/dirD/.."])
_check(p, "dir*/**/fileC", ["dirC/fileC"]) _check(p, "dir*/**/fileC", ["dirC/fileC"])
_check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"]) _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"])
_check(p, "*/dirD/**", ["dirC/dirD", "dirC/dirD/fileD"]) _check(p, "*/dirD/**", ["dirC/dirD/", "dirC/dirD/fileD"])
_check(p, "*/dirD/**/", ["dirC/dirD/"]) _check(p, "*/dirD/**/", ["dirC/dirD/"])
@needs_symlinks @needs_symlinks
@ -1827,19 +1827,19 @@ class DummyPathTest(DummyPurePathTest):
_check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/"]) _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/"])
_check(p, "dir*/*/..", ["dirC/dirD/.."]) _check(p, "dir*/*/..", ["dirC/dirD/.."])
_check(p, "dir*/**", [ _check(p, "dir*/**", [
"dirA", "dirA/linkC", "dirA/", "dirA/linkC",
"dirB", "dirB/fileB", "dirB/linkD", "dirB/", "dirB/fileB", "dirB/linkD",
"dirC", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt", "dirC/", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt",
"dirE"]) "dirE/"])
_check(p, "dir*/**/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"]) _check(p, "dir*/**/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "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/", "dirC/dirD/fileD"])
_check(p, "dir*/*/**/", ["dirC/dirD/"]) _check(p, "dir*/*/**/", ["dirC/dirD/"])
_check(p, "dir*/*/**/..", ["dirC/dirD/.."]) _check(p, "dir*/*/**/..", ["dirC/dirD/.."])
_check(p, "dir*/**/fileC", ["dirC/fileC"]) _check(p, "dir*/**/fileC", ["dirC/fileC"])
_check(p, "dir*/*/../dirD/**", ["dirC/dirD/../dirD", "dirC/dirD/../dirD/fileD"]) _check(p, "dir*/*/../dirD/**", ["dirC/dirD/../dirD/", "dirC/dirD/../dirD/fileD"])
_check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"]) _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"])
_check(p, "*/dirD/**", ["dirC/dirD", "dirC/dirD/fileD"]) _check(p, "*/dirD/**", ["dirC/dirD/", "dirC/dirD/fileD"])
_check(p, "*/dirD/**/", ["dirC/dirD/"]) _check(p, "*/dirD/**/", ["dirC/dirD/"])
def test_rglob_common(self): def test_rglob_common(self):
@ -1876,13 +1876,13 @@ class DummyPathTest(DummyPurePathTest):
"dirC/dirD", "dirC/dirD/fileD"]) "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("**/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/", "dirC/dirD/fileD"])
_check(p.rglob("dir*/**/"), ["dirC/dirD/"]) _check(p.rglob("dir*/**/"), ["dirC/dirD/"])
_check(p.rglob("*/*"), ["dirC/dirD/fileD"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
_check(p.rglob("*/"), ["dirC/dirD/"]) _check(p.rglob("*/"), ["dirC/dirD/"])
_check(p.rglob(""), ["dirC/", "dirC/dirD/"]) _check(p.rglob(""), ["dirC/", "dirC/dirD/"])
_check(p.rglob("**"), [ _check(p.rglob("**"), [
"dirC", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt"]) "dirC/", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt"])
_check(p.rglob("**/"), ["dirC/", "dirC/dirD/"]) _check(p.rglob("**/"), ["dirC/", "dirC/dirD/"])
# gh-91616, a re module regression # gh-91616, a re module regression
_check(p.rglob("*.txt"), ["dirC/novel.txt"]) _check(p.rglob("*.txt"), ["dirC/novel.txt"])