mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +00:00
GH-102613: Fix recursion error from pathlib.Path.glob()
(GH-104373)
Use `Path.walk()` to implement the recursive wildcard `**`. This method uses an iterative (rather than recursive) walk - see GH-100282.
This commit is contained in:
parent
b378d991f8
commit
cb88ae635e
3 changed files with 18 additions and 20 deletions
|
@ -164,30 +164,15 @@ class _RecursiveWildcardSelector(_Selector):
|
||||||
def __init__(self, pat, child_parts, flavour, case_sensitive):
|
def __init__(self, pat, child_parts, flavour, case_sensitive):
|
||||||
_Selector.__init__(self, child_parts, flavour, case_sensitive)
|
_Selector.__init__(self, child_parts, flavour, case_sensitive)
|
||||||
|
|
||||||
def _iterate_directories(self, parent_path, scandir):
|
def _iterate_directories(self, parent_path):
|
||||||
yield parent_path
|
yield parent_path
|
||||||
try:
|
for dirpath, dirnames, _ in parent_path.walk():
|
||||||
# We must close the scandir() object before proceeding to
|
for dirname in dirnames:
|
||||||
# avoid exhausting file descriptors when globbing deep trees.
|
yield dirpath._make_child_relpath(dirname)
|
||||||
with scandir(parent_path) as scandir_it:
|
|
||||||
entries = list(scandir_it)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
for entry in entries:
|
|
||||||
entry_is_dir = False
|
|
||||||
try:
|
|
||||||
entry_is_dir = entry.is_dir(follow_symlinks=False)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
if entry_is_dir:
|
|
||||||
path = parent_path._make_child_relpath(entry.name)
|
|
||||||
for p in self._iterate_directories(path, scandir):
|
|
||||||
yield p
|
|
||||||
|
|
||||||
def _select_from(self, parent_path, scandir):
|
def _select_from(self, parent_path, scandir):
|
||||||
successor_select = self.successor._select_from
|
successor_select = self.successor._select_from
|
||||||
for starting_point in self._iterate_directories(parent_path, scandir):
|
for starting_point in self._iterate_directories(parent_path):
|
||||||
for p in successor_select(starting_point, scandir):
|
for p in successor_select(starting_point, scandir):
|
||||||
yield p
|
yield p
|
||||||
|
|
||||||
|
|
|
@ -1972,6 +1972,17 @@ class _BasePathTest(object):
|
||||||
bad_link.symlink_to("bad" * 200)
|
bad_link.symlink_to("bad" * 200)
|
||||||
self.assertEqual(sorted(base.glob('**/*')), [bad_link])
|
self.assertEqual(sorted(base.glob('**/*')), [bad_link])
|
||||||
|
|
||||||
|
def test_glob_above_recursion_limit(self):
|
||||||
|
recursion_limit = 40
|
||||||
|
# directory_depth > recursion_limit
|
||||||
|
directory_depth = recursion_limit + 10
|
||||||
|
base = pathlib.Path(os_helper.TESTFN, 'deep')
|
||||||
|
path = pathlib.Path(base, *(['d'] * directory_depth))
|
||||||
|
path.mkdir(parents=True)
|
||||||
|
|
||||||
|
with set_recursion_limit(recursion_limit):
|
||||||
|
list(base.glob('**'))
|
||||||
|
|
||||||
def _check_resolve(self, p, expected, strict=True):
|
def _check_resolve(self, p, expected, strict=True):
|
||||||
q = p.resolve(strict)
|
q = p.resolve(strict)
|
||||||
self.assertEqual(q, expected)
|
self.assertEqual(q, expected)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix issue where :meth:`pathlib.Path.glob` raised :exc:`RecursionError` when
|
||||||
|
walking deep directory trees.
|
Loading…
Add table
Add a link
Reference in a new issue