summaryrefslogtreecommitdiff
path: root/Lib
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2023-05-03 00:16:04 +0100
committerGitHub <noreply@github.com>2023-05-02 23:16:04 +0000
commit65a49c6553a27cc36eebb4b79f409c3cb4450d8c (patch)
tree63dd98d6a2c362deee109cfc92e8f83c98dca7d8 /Lib
parent47770a1e91d096fd1c689eb0c78b0f9e76b43639 (diff)
downloadcpython-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.py12
-rw-r--r--Lib/test/test_pathlib.py5
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):