Issue #21058: Fix a leak of file descriptor in tempfile.NamedTemporaryFile(),
close the file descriptor if os.fdopen() fails
diff --git a/Lib/tempfile.py b/Lib/tempfile.py
index 7154d2c..94ad0b7 100644
--- a/Lib/tempfile.py
+++ b/Lib/tempfile.py
@@ -460,8 +460,12 @@
flags |= _os.O_TEMPORARY
(fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
- file = _os.fdopen(fd, mode, bufsize)
- return _TemporaryFileWrapper(file, name, delete)
+ try:
+ file = _os.fdopen(fd, mode, bufsize)
+ return _TemporaryFileWrapper(file, name, delete)
+ except Exception:
+ _os.close(fd)
+ raise
if _os.name != 'posix' or _os.sys.platform == 'cygwin':
# On non-POSIX and Cygwin systems, assume that we cannot unlink a file
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index 40f70c0..465bcda 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -771,6 +771,24 @@
pass
self.assertRaises(ValueError, use_closed)
+ def test_no_leak_fd(self):
+ # Issue #21058: don't leak file descriptor when fdopen() fails
+ old_close = os.close
+ old_fdopen = os.fdopen
+ closed = []
+ def close(fd):
+ closed.append(fd)
+ def fdopen(*args):
+ raise ValueError()
+ os.close = close
+ os.fdopen = fdopen
+ try:
+ self.assertRaises(ValueError, tempfile.NamedTemporaryFile)
+ self.assertEqual(len(closed), 1)
+ finally:
+ os.close = old_close
+ os.fdopen = old_fdopen
+
# How to test the mode and bufsize parameters?
test_classes.append(test_NamedTemporaryFile)
diff --git a/Misc/NEWS b/Misc/NEWS
index e2c757c..0f45555 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,9 @@
Library
-------
+- Issue #21058: Fix a leak of file descriptor in tempfile.NamedTemporaryFile(),
+ close the file descriptor if os.fdopen() fails
+
- Issue #20283: RE pattern methods now accept the string keyword parameters
as documented. The pattern and source keyword parameters are left as
deprecated aliases.