]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] GH-89727: Fix FD leak on `os.fwalk()` generator finalization. (GH-119766)...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 30 May 2024 04:30:37 +0000 (06:30 +0200)
committerGitHub <noreply@github.com>
Thu, 30 May 2024 04:30:37 +0000 (05:30 +0100)
GH-89727: Fix FD leak on `os.fwalk()` generator finalization. (GH-119766)

Follow-up to 3c890b50. Ensure we `os.close()` open file descriptors when
the `os.fwalk()` generator is finalized.
(cherry picked from commit a5fef800d31648d19cecc240a2fa0dc71371753e)

Co-authored-by: Barney Gale <barney.gale@gmail.com>
Lib/os.py
Lib/test/test_os.py

index 3e911c9b61b40ca23a7561628cbef23b9da20533..5619470275b87501724802ca1cce8869ea1fdb77 100644 (file)
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -478,8 +478,15 @@ if {open, stat} <= supports_dir_fd and {scandir, stat} <= supports_fd:
         top = fspath(top)
         stack = [(_fwalk_walk, (True, dir_fd, top, top, None))]
         isbytes = isinstance(top, bytes)
-        while stack:
-            yield from _fwalk(stack, isbytes, topdown, onerror, follow_symlinks)
+        try:
+            while stack:
+                yield from _fwalk(stack, isbytes, topdown, onerror, follow_symlinks)
+        finally:
+            # Close any file descriptors still on the stack.
+            while stack:
+                action, value = stack.pop()
+                if action == _fwalk_close:
+                    close(value)
 
     # Each item in the _fwalk() stack is a pair (action, args).
     _fwalk_walk = 0  # args: (isroot, dirfd, toppath, topname, entry)
index a764632eb95f1d5ff1a38d367a7f0aabbd109fe4..88037059e96b9914d33265ae420f7ab4720cdf90 100644 (file)
@@ -1671,6 +1671,27 @@ class FwalkTests(WalkTests):
         self.addCleanup(os.close, newfd)
         self.assertEqual(newfd, minfd)
 
+    @unittest.skipIf(
+        support.is_emscripten, "Cannot dup stdout on Emscripten"
+    )
+    @unittest.skipIf(
+        support.is_android, "dup return value is unpredictable on Android"
+    )
+    def test_fd_finalization(self):
+        # Check that close()ing the fwalk() generator closes FDs
+        def getfd():
+            fd = os.dup(1)
+            os.close(fd)
+            return fd
+        for topdown in (False, True):
+            old_fd = getfd()
+            it = self.fwalk(os_helper.TESTFN, topdown=topdown)
+            self.assertEqual(getfd(), old_fd)
+            next(it)
+            self.assertGreater(getfd(), old_fd)
+            it.close()
+            self.assertEqual(getfd(), old_fd)
+
     # fwalk() keeps file descriptors open
     test_walk_many_open_files = None