Skip to content

Commit 65a49c6

Browse files
authored
pythonGH-104102: Optimize pathlib.Path.glob() handling of ../ pattern segments (pythonGH-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.
1 parent 47770a1 commit 65a49c6

File tree

3 files changed

+19
-0
lines changed

3 files changed

+19
-0
lines changed

Lib/pathlib.py

+12
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def _make_selector(pattern_parts, flavour):
7474
return _TerminatingSelector()
7575
if pat == '**':
7676
cls = _RecursiveWildcardSelector
77+
elif pat == '..':
78+
cls = _ParentSelector
7779
elif '**' in pat:
7880
raise ValueError("Invalid pattern: '**' can only be an entire path component")
7981
elif _is_wildcard_pattern(pat):
@@ -114,6 +116,16 @@ def _select_from(self, parent_path, is_dir, exists, scandir):
114116
yield parent_path
115117

116118

119+
class _ParentSelector(_Selector):
120+
def __init__(self, name, child_parts, flavour):
121+
_Selector.__init__(self, child_parts, flavour)
122+
123+
def _select_from(self, parent_path, is_dir, exists, scandir):
124+
path = parent_path._make_child_relpath('..')
125+
for p in self.successor._select_from(path, is_dir, exists, scandir):
126+
yield p
127+
128+
117129
class _PreciseSelector(_Selector):
118130

119131
def __init__(self, name, child_parts, flavour):

Lib/test/test_pathlib.py

+5
Original file line numberDiff line numberDiff line change
@@ -1892,8 +1892,13 @@ def test_glob_dotdot(self):
18921892
P = self.cls
18931893
p = P(BASE)
18941894
self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1895+
self.assertEqual(set(p.glob("../..")), { P(BASE, "..", "..") })
1896+
self.assertEqual(set(p.glob("dirA/..")), { P(BASE, "dirA", "..") })
18951897
self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1898+
self.assertEqual(set(p.glob("dirA/../file*/..")), set())
18961899
self.assertEqual(set(p.glob("../xyzzy")), set())
1900+
self.assertEqual(set(p.glob("xyzzy/..")), set())
1901+
self.assertEqual(set(p.glob("/".join([".."] * 50))), { P(BASE, *[".."] * 50)})
18971902

18981903
@os_helper.skip_unless_symlink
18991904
def test_glob_permissions(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve performance of :meth:`pathlib.Path.glob` when evaluating patterns
2+
that contain ``'../'`` segments.

0 commit comments

Comments
 (0)