Skip to content

Commit

Permalink
pythonGH-102613: Fix recursion error from pathlib.Path.glob()
Browse files Browse the repository at this point in the history
Use `Path.walk()` to implement the recursive wildcard `**`. This method
uses an iterative (rather than recursive) walk - see pythonGH-100282.
  • Loading branch information
barneygale committed May 11, 2023
1 parent 94f30c7 commit 71dc988
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 20 deletions.
25 changes: 5 additions & 20 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,30 +164,15 @@ class _RecursiveWildcardSelector(_Selector):
def __init__(self, pat, 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
try:
# We must close the scandir() object before proceeding to
# avoid exhausting file descriptors when globbing deep trees.
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
for dirpath, dirnames, _ in parent_path.walk():
for dirname in dirnames:
yield dirpath._make_child_relpath(dirname)

def _select_from(self, parent_path, scandir):
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):
yield p

Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,17 @@ def test_glob_long_symlink(self):
bad_link.symlink_to("bad" * 200)
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):
q = p.resolve(strict)
self.assertEqual(q, expected)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix issue where :meth:`pathlib.Path.glob` raised :exc:`RecursionError` when
walking deep directory trees.

0 comments on commit 71dc988

Please sign in to comment.