]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-102613: Fix recursion error from `pathlib.Path.glob()` (GH-104373)
authorBarney Gale <barney.gale@gmail.com>
Mon, 15 May 2023 17:33:32 +0000 (18:33 +0100)
committerGitHub <noreply@github.com>
Mon, 15 May 2023 17:33:32 +0000 (18:33 +0100)
Use `Path.walk()` to implement the recursive wildcard `**`. This method
uses an iterative (rather than recursive) walk - see GH-100282.

Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS.d/next/Library/2023-05-11-01-07-42.gh-issue-102613.uMsokt.rst [new file with mode: 0644]

index 40b72930e1f08aec57aee2b52a84c9edd0b96d7e..ef7c47c9e775e4e7d87519c1e76989d2c5ad7072 100644 (file)
@@ -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
 
index 67ca4795962b3eb431b123f59e1a7f3984bc71b6..46a5248499c5d0b384cb01505c798cb276483068 100644 (file)
@@ -1972,6 +1972,17 @@ class _BasePathTest(object):
         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)
diff --git a/Misc/NEWS.d/next/Library/2023-05-11-01-07-42.gh-issue-102613.uMsokt.rst b/Misc/NEWS.d/next/Library/2023-05-11-01-07-42.gh-issue-102613.uMsokt.rst
new file mode 100644 (file)
index 0000000..3b06964
--- /dev/null
@@ -0,0 +1,2 @@
+Fix issue where :meth:`pathlib.Path.glob` raised :exc:`RecursionError` when
+walking deep directory trees.