diff options
author | Barney Gale <barney.gale@gmail.com> | 2023-05-03 00:16:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-02 23:16:04 +0000 |
commit | 65a49c6553a27cc36eebb4b79f409c3cb4450d8c (patch) | |
tree | 63dd98d6a2c362deee109cfc92e8f83c98dca7d8 /Lib | |
parent | 47770a1e91d096fd1c689eb0c78b0f9e76b43639 (diff) | |
download | cpython-git-65a49c6553a27cc36eebb4b79f409c3cb4450d8c.tar.gz |
GH-104102: Optimize `pathlib.Path.glob()` handling of `../` pattern segments (GH-104103)
These segments do not require a `stat()` call, as the selector's
`_select_from()` method is called after we've established that the
parent is a directory.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/pathlib.py | 12 | ||||
-rw-r--r-- | Lib/test/test_pathlib.py | 5 |
2 files changed, 17 insertions, 0 deletions
diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 61e7f3e443..c69089f4e1 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -74,6 +74,8 @@ def _make_selector(pattern_parts, flavour): return _TerminatingSelector() if pat == '**': cls = _RecursiveWildcardSelector + elif pat == '..': + cls = _ParentSelector elif '**' in pat: raise ValueError("Invalid pattern: '**' can only be an entire path component") elif _is_wildcard_pattern(pat): @@ -114,6 +116,16 @@ class _TerminatingSelector: yield parent_path +class _ParentSelector(_Selector): + def __init__(self, name, child_parts, flavour): + _Selector.__init__(self, child_parts, flavour) + + def _select_from(self, parent_path, is_dir, exists, scandir): + path = parent_path._make_child_relpath('..') + for p in self.successor._select_from(path, is_dir, exists, scandir): + yield p + + class _PreciseSelector(_Selector): def __init__(self, name, child_parts, flavour): diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 8b5b61a818..9902b72422 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1892,8 +1892,13 @@ class _BasePathTest(object): P = self.cls p = P(BASE) self.assertEqual(set(p.glob("..")), { P(BASE, "..") }) + self.assertEqual(set(p.glob("../..")), { P(BASE, "..", "..") }) + self.assertEqual(set(p.glob("dirA/..")), { P(BASE, "dirA", "..") }) self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) + self.assertEqual(set(p.glob("dirA/../file*/..")), set()) self.assertEqual(set(p.glob("../xyzzy")), set()) + self.assertEqual(set(p.glob("xyzzy/..")), set()) + self.assertEqual(set(p.glob("/".join([".."] * 50))), { P(BASE, *[".."] * 50)}) @os_helper.skip_unless_symlink def test_glob_permissions(self): |