mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
47770a1e91
commit
65a49c6553
|
@ -74,6 +74,8 @@ def _make_selector(pattern_parts, flavour):
|
||||||
return _TerminatingSelector()
|
return _TerminatingSelector()
|
||||||
if pat == '**':
|
if pat == '**':
|
||||||
cls = _RecursiveWildcardSelector
|
cls = _RecursiveWildcardSelector
|
||||||
|
elif pat == '..':
|
||||||
|
cls = _ParentSelector
|
||||||
elif '**' in pat:
|
elif '**' in pat:
|
||||||
raise ValueError("Invalid pattern: '**' can only be an entire path component")
|
raise ValueError("Invalid pattern: '**' can only be an entire path component")
|
||||||
elif _is_wildcard_pattern(pat):
|
elif _is_wildcard_pattern(pat):
|
||||||
|
@ -114,6 +116,16 @@ class _TerminatingSelector:
|
||||||
yield parent_path
|
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):
|
class _PreciseSelector(_Selector):
|
||||||
|
|
||||||
def __init__(self, name, child_parts, flavour):
|
def __init__(self, name, child_parts, flavour):
|
||||||
|
|
|
@ -1892,8 +1892,13 @@ class _BasePathTest(object):
|
||||||
P = self.cls
|
P = self.cls
|
||||||
p = P(BASE)
|
p = P(BASE)
|
||||||
self.assertEqual(set(p.glob("..")), { 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*")), { 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("xyzzy/..")), set())
|
||||||
|
self.assertEqual(set(p.glob("/".join([".."] * 50))), { P(BASE, *[".."] * 50)})
|
||||||
|
|
||||||
@os_helper.skip_unless_symlink
|
@os_helper.skip_unless_symlink
|
||||||
def test_glob_permissions(self):
|
def test_glob_permissions(self):
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Improve performance of :meth:`pathlib.Path.glob` when evaluating patterns
|
||||||
|
that contain ``'../'`` segments.
|
Loading…
Reference in New Issue