Skip to content

Commit 083bf3a

Browse files
[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 3c890b5. Ensure we `os.close()` open file descriptors when the `os.fwalk()` generator is finalized. (cherry picked from commit a5fef80) Co-authored-by: Barney Gale <barney.gale@gmail.com>
1 parent 810a09a commit 083bf3a

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

Lib/os.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,15 @@ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=
480480
top = fspath(top)
481481
stack = [(_fwalk_walk, (True, dir_fd, top, top, None))]
482482
isbytes = isinstance(top, bytes)
483-
while stack:
484-
yield from _fwalk(stack, isbytes, topdown, onerror, follow_symlinks)
483+
try:
484+
while stack:
485+
yield from _fwalk(stack, isbytes, topdown, onerror, follow_symlinks)
486+
finally:
487+
# Close any file descriptors still on the stack.
488+
while stack:
489+
action, value = stack.pop()
490+
if action == _fwalk_close:
491+
close(value)
485492

486493
# Each item in the _fwalk() stack is a pair (action, args).
487494
_fwalk_walk = 0 # args: (isroot, dirfd, toppath, topname, entry)

Lib/test/test_os.py

+21
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,27 @@ def test_fd_leak(self):
16851685
self.addCleanup(os.close, newfd)
16861686
self.assertEqual(newfd, minfd)
16871687

1688+
@unittest.skipIf(
1689+
support.is_emscripten, "Cannot dup stdout on Emscripten"
1690+
)
1691+
@unittest.skipIf(
1692+
support.is_android, "dup return value is unpredictable on Android"
1693+
)
1694+
def test_fd_finalization(self):
1695+
# Check that close()ing the fwalk() generator closes FDs
1696+
def getfd():
1697+
fd = os.dup(1)
1698+
os.close(fd)
1699+
return fd
1700+
for topdown in (False, True):
1701+
old_fd = getfd()
1702+
it = self.fwalk(os_helper.TESTFN, topdown=topdown)
1703+
self.assertEqual(getfd(), old_fd)
1704+
next(it)
1705+
self.assertGreater(getfd(), old_fd)
1706+
it.close()
1707+
self.assertEqual(getfd(), old_fd)
1708+
16881709
# fwalk() keeps file descriptors open
16891710
test_walk_many_open_files = None
16901711

0 commit comments

Comments
 (0)