https://github.com/python/cpython/commit/083bf3ad1795604ee833d1595f2a536b9cd57419
commit: 083bf3ad1795604ee833d1595f2a536b9cd57419
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: barneygale <[email protected]>
date: 2024-05-30T04:40:21Z
summary:

[3.13] GH-89727: Fix FD leak on `os.fwalk()` generator finalization. 
(GH-119766) (#119767)

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 <[email protected]>

files:
M Lib/os.py
M Lib/test/test_os.py

diff --git a/Lib/os.py b/Lib/os.py
index cef5d90a8c23df..0408e2db79e66e 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -480,8 +480,15 @@ def fwalk(top=".", topdown=True, onerror=None, *, 
follow_symlinks=False, dir_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)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 7dc5784820e847..de5a86f676c4d5 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -1685,6 +1685,27 @@ def test_fd_leak(self):
         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
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to