Skip to content

gh-126639: Add ResourceWarning to NamedTemporaryFile #126677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
26 changes: 23 additions & 3 deletions Lib/tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,19 @@ class _TemporaryFileCloser:
cleanup_called = False
close_called = False

def __init__(self, file, name, delete=True, delete_on_close=True):
def __init__(
self,
file,
name,
delete=True,
delete_on_close=True,
warn_message="Implicitly cleaning up unknown file",
):
self.file = file
self.name = name
self.delete = delete
self.delete_on_close = delete_on_close
self.warn_message = warn_message

def cleanup(self, windows=(_os.name == 'nt'), unlink=_os.unlink):
if not self.cleanup_called:
Expand Down Expand Up @@ -469,7 +477,10 @@ def close(self):
self.cleanup()

def __del__(self):
close_called = self.close_called
self.cleanup()
if not close_called:
_warnings.warn(self.warn_message, ResourceWarning)


class _TemporaryFileWrapper:
Expand All @@ -483,8 +494,17 @@ class _TemporaryFileWrapper:
def __init__(self, file, name, delete=True, delete_on_close=True):
self.file = file
self.name = name
self._closer = _TemporaryFileCloser(file, name, delete,
delete_on_close)
self._closer = _TemporaryFileCloser(
file,
name,
delete,
delete_on_close,
warn_message=f"Implicitly cleaning up {self!r}",
)

def __repr__(self):
file = self.__dict__['file']
return f"<{type(self).__name__} {file=}>"

def __getattr__(self, name):
# Attribute lookups are delegated to the underlying file
Expand Down
9 changes: 6 additions & 3 deletions Lib/test/test_tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1112,11 +1112,14 @@ def my_func(dir):
# 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)
with self.assertWarnsRegex(
expected_warning=ResourceWarning,
expected_regex=r"Implicitly cleaning up <_TemporaryFileWrapper file=.*>",
):
tmp_name = my_func(dir)
support.gc_collect()
self.assertFalse(os.path.exists(tmp_name),
f"NamedTemporaryFile {tmp_name!r} "
f"exists after finalizer ")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:class:`tempfile.NamedTemporaryFile` will now issue a :exc:`ResourceWarning` when it is finalized by the garbage collector without being explicitly closed.
Loading