https://github.com/python/cpython/commit/d4a146d56727f456e579419cbb2bb72e6667103a commit: d4a146d56727f456e579419cbb2bb72e6667103a branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: barneygale <[email protected]> date: 2024-05-30T05:30:37+01:00 summary:
[3.12] GH-89727: Fix FD leak on `os.fwalk()` generator finalization. (GH-119766) (#119768) 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 3e911c9b61b40c..5619470275b875 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -478,8 +478,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 a764632eb95f1d..88037059e96b99 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1671,6 +1671,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]
