gh-58451: Add optional delete_on_close parameter to NamedTemporaryFile (GH-97015)

This commit is contained in:
Ev2geny 2022-10-05 00:37:33 +02:00 committed by GitHub
parent bbc7cd649a
commit 743453a554
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 215 additions and 50 deletions

View file

@ -11,6 +11,7 @@ import contextlib
import stat
import types
import weakref
import gc
from unittest import mock
import unittest
@ -1013,6 +1014,102 @@ class TestNamedTemporaryFile(BaseTestCase):
pass
self.assertRaises(ValueError, use_closed)
def test_context_man_not_del_on_close_if_delete_on_close_false(self):
# Issue gh-58451: tempfile.NamedTemporaryFile is not particulary useful
# on Windows
# A NamedTemporaryFile is NOT deleted when closed if
# delete_on_close=False, but is deleted on context manager exit
dir = tempfile.mkdtemp()
try:
with tempfile.NamedTemporaryFile(dir=dir,
delete=True,
delete_on_close=False) as f:
f.write(b'blat')
f_name = f.name
f.close()
with self.subTest():
# Testing that file is not deleted on close
self.assertTrue(os.path.exists(f.name),
f"NamedTemporaryFile {f.name!r} is incorrectly "
f"deleted on closure when delete_on_close=False")
with self.subTest():
# Testing that file is deleted on context manager exit
self.assertFalse(os.path.exists(f.name),
f"NamedTemporaryFile {f.name!r} exists "
f"after context manager exit")
finally:
os.rmdir(dir)
def test_context_man_ok_to_delete_manually(self):
# In the case of delete=True, a NamedTemporaryFile can be manually
# deleted in a with-statement context without causing an error.
dir = tempfile.mkdtemp()
try:
with tempfile.NamedTemporaryFile(dir=dir,
delete=True,
delete_on_close=False) as f:
f.write(b'blat')
f.close()
os.unlink(f.name)
finally:
os.rmdir(dir)
def test_context_man_not_del_if_delete_false(self):
# A NamedTemporaryFile is not deleted if delete = False
dir = tempfile.mkdtemp()
f_name = ""
try:
# Test that delete_on_close=True has no effect if delete=False.
with tempfile.NamedTemporaryFile(dir=dir, delete=False,
delete_on_close=True) as f:
f.write(b'blat')
f_name = f.name
self.assertTrue(os.path.exists(f.name),
f"NamedTemporaryFile {f.name!r} exists after close")
finally:
os.unlink(f_name)
os.rmdir(dir)
def test_del_by_finalizer(self):
# A NamedTemporaryFile is deleted when finalized in the case of
# delete=True, delete_on_close=False, and no with-statement is used.
def my_func(dir):
f = tempfile.NamedTemporaryFile(dir=dir, delete=True,
delete_on_close=False)
tmp_name = f.name
f.write(b'blat')
# Testing extreme case, where the file is not explicitly closed
# f.close()
return tmp_name
# Make sure that the garbage collector has finalized the file object.
gc.collect()
dir = tempfile.mkdtemp()
try:
tmp_name = my_func(dir)
self.assertFalse(os.path.exists(tmp_name),
f"NamedTemporaryFile {tmp_name!r} "
f"exists after finalizer ")
finally:
os.rmdir(dir)
def test_correct_finalizer_work_if_already_deleted(self):
# There should be no error in the case of delete=True,
# delete_on_close=False, no with-statement is used, and the file is
# deleted manually.
def my_func(dir)->str:
f = tempfile.NamedTemporaryFile(dir=dir, delete=True,
delete_on_close=False)
tmp_name = f.name
f.write(b'blat')
f.close()
os.unlink(tmp_name)
return tmp_name
# Make sure that the garbage collector has finalized the file object.
gc.collect()
def test_bad_mode(self):
dir = tempfile.mkdtemp()
self.addCleanup(os_helper.rmtree, dir)
@ -1081,7 +1178,8 @@ class TestSpooledTemporaryFile(BaseTestCase):
missing_attrs = iobase_attrs - spooledtempfile_attrs
self.assertFalse(
missing_attrs,
'SpooledTemporaryFile missing attributes from IOBase/BufferedIOBase/TextIOBase'
'SpooledTemporaryFile missing attributes from '
'IOBase/BufferedIOBase/TextIOBase'
)
def test_del_on_close(self):