Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cb88ae6

Browse filesBrowse files
authored
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.
1 parent b378d99 commit cb88ae6
Copy full SHA for cb88ae6

File tree

3 files changed

+18
-20
lines changed
Filter options

3 files changed

+18
-20
lines changed

‎Lib/pathlib.py

Copy file name to clipboardExpand all lines: Lib/pathlib.py
+5-20Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -164,30 +164,15 @@ class _RecursiveWildcardSelector(_Selector):
164164
def __init__(self, pat, child_parts, flavour, case_sensitive):
165165
_Selector.__init__(self, child_parts, flavour, case_sensitive)
166166

167-
def _iterate_directories(self, parent_path, scandir):
167+
def _iterate_directories(self, parent_path):
168168
yield parent_path
169-
try:
170-
# We must close the scandir() object before proceeding to
171-
# avoid exhausting file descriptors when globbing deep trees.
172-
with scandir(parent_path) as scandir_it:
173-
entries = list(scandir_it)
174-
except OSError:
175-
pass
176-
else:
177-
for entry in entries:
178-
entry_is_dir = False
179-
try:
180-
entry_is_dir = entry.is_dir(follow_symlinks=False)
181-
except OSError:
182-
pass
183-
if entry_is_dir:
184-
path = parent_path._make_child_relpath(entry.name)
185-
for p in self._iterate_directories(path, scandir):
186-
yield p
169+
for dirpath, dirnames, _ in parent_path.walk():
170+
for dirname in dirnames:
171+
yield dirpath._make_child_relpath(dirname)
187172

188173
def _select_from(self, parent_path, scandir):
189174
successor_select = self.successor._select_from
190-
for starting_point in self._iterate_directories(parent_path, scandir):
175+
for starting_point in self._iterate_directories(parent_path):
191176
for p in successor_select(starting_point, scandir):
192177
yield p
193178

‎Lib/test/test_pathlib.py

Copy file name to clipboardExpand all lines: Lib/test/test_pathlib.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,17 @@ def test_glob_long_symlink(self):
19721972
bad_link.symlink_to("bad" * 200)
19731973
self.assertEqual(sorted(base.glob('**/*')), [bad_link])
19741974

1975+
def test_glob_above_recursion_limit(self):
1976+
recursion_limit = 40
1977+
# directory_depth > recursion_limit
1978+
directory_depth = recursion_limit + 10
1979+
base = pathlib.Path(os_helper.TESTFN, 'deep')
1980+
path = pathlib.Path(base, *(['d'] * directory_depth))
1981+
path.mkdir(parents=True)
1982+
1983+
with set_recursion_limit(recursion_limit):
1984+
list(base.glob('**'))
1985+
19751986
def _check_resolve(self, p, expected, strict=True):
19761987
q = p.resolve(strict)
19771988
self.assertEqual(q, expected)
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix issue where :meth:`pathlib.Path.glob` raised :exc:`RecursionError` when
2+
walking deep directory trees.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.