summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2024-05-30 04:40:21 (GMT)
committerGitHub <noreply@github.com>2024-05-30 04:40:21 (GMT)
commit083bf3ad1795604ee833d1595f2a536b9cd57419 (patch)
tree7b21acd90198472279cffb68fe26f20c76fad447 /Lib
parent810a09ad3710be60cff9e174be85ca65e76cdbd1 (diff)
downloadcpython-083bf3ad1795604ee833d1595f2a536b9cd57419.zip
cpython-083bf3ad1795604ee833d1595f2a536b9cd57419.tar.gz
cpython-083bf3ad1795604ee833d1595f2a536b9cd57419.tar.bz2
[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 <barney.gale@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/os.py11
-rw-r--r--Lib/test/test_os.py21
2 files changed, 30 insertions, 2 deletions
diff --git a/Lib/os.py b/Lib/os.py
index cef5d90..0408e2d 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -480,8 +480,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)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 7dc5784..de5a86f 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -1685,6 +1685,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