Uh oh!
There was an error while loading. Please reload this page.
gh-58451: Add optional delete_on_close parameter to NamedTemporaryFile - #97015
Conversation
…pdated to handle the situation, when delete = True, delete_on_close = Flase and no context manager was used
The changes done to the code since it left the PR 22431
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Éric <merwok@netwok.org>
Ev2geny
commented
Sep 22, 2022
Ok, looks like there are some Ubuntu errors, which I will need to look at |
Uh oh!
There was an error while loading. Please reload this page.
…r to _TemporaryFileCloser Test test_del_by_finalizer_if_no_with is changed from simulating finalizer by calling __del__() to making sure finalizer really runs
Uh oh!
There was an error while loading. Please reload this page.
…close is moved to _TemporaryFileWrapper based on the advice from @eryksun There are errors in the test (at least on Linux)
… this is a solution or workaround
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…ch takes into account delete_on_close moved to _TemporaryFileWrapper (suggestion of @ekisun)
@eryksun , I have implemented all suggestions, but it now fails the unit test for the issue18879 deftest_method_lookup(self):
# Issue #18879: Looking up a temporary file method should keep it# alive long enough.f=self.do_create()
wr=weakref.ref(f)
write=f.writewrite2=f.writedelfwrite(b'foo') |
The delegated attribute cache is a problem. It leads to a reference cycle if the function wrapper (bpo-18879) is changed to keep a reference to the file wrapper (i.e. So, sorry to have misled you, but it looks like we're going to have to implement all delete operations in the closer. The wrapper just needs to change class_TemporaryFileCloser:
"""A separate object allowing proper closing of a temporary file's underlying file object, without adding a __del__ method to the temporary file."""cleanup_called=Falseclose_called=Falsedef__init__(self, file, name, delete=True, delete_on_close=True):
self.file=fileself.name=nameself.delete=deleteself.delete_on_close=delete_on_closedefcleanup(self, windows=(_os.name=='nt'), unlink=_os.unlink):
ifnotself.cleanup_called:
self.cleanup_called=Truetry:
ifnotself.close_called:
self.close_called=Trueself.file.close()
finally:
ifself.deleteandnot (self.delete_on_closeandwindows):
try:
unlink(self.name)
exceptFileNotFoundError:
passdefclose(self):
ifnotself.close_called:
self.close_called=Truetry:
self.file.close()
finally:
ifself.deleteandself.delete_on_close:
self.cleanup()
def__del__(self):
self.cleanup()class_TemporaryFileWrapper:
"""Temporary file wrapper This class provides a wrapper around files opened for temporary use. In particular, it seeks to automatically remove the file when it is no longer needed. """def__init__(self, file, name, delete=True, delete_on_close=True):
self.file=fileself.name=nameself._closer=_TemporaryFileCloser(file, name, delete,
delete_on_close)
def__getattr__(self, name):
# Attribute lookups are delegated to the underlying file# and cached for non-numeric results# (i.e. methods are cached, closed and friends are not)file=self.__dict__['file']
a=getattr(file, name)
ifhasattr(a, '__call__'):
func=a@_functools.wraps(func)deffunc_wrapper(*args, **kwargs):
returnfunc(*args, **kwargs)
# Avoid closing the file as long as the wrapper is alive,# see issue #18879.func_wrapper._closer=self._closera=func_wrapperifnotisinstance(a, int):
setattr(self, name, a)
returna# The underlying __enter__ method returns the wrong object# (self.file) so override it to return the wrapperdef__enter__(self):
self.file.__enter__()
returnself# Need to trap __exit__ as well to ensure the file gets# deleted when used in a with statementdef__exit__(self, exc, value, tb):
result=self.file.__exit__(exc, value, tb)
self._closer.cleanup()
returnresultdefclose(self):
""" Close the temporary file, possibly deleting it. """self._closer.close()
# iter() doesn't use __getattr__ to find the __iter__ methoddef__iter__(self):
# Don't return iter(self.file), but yield from it to avoid closing# file as long as it's being used as iterator (see issue #23700). We# can't use 'yield from' here because iter(file) returns the file# object itself, which has a close method, and thus the file would get# closed when the generator is finalized, due to PEP380 semantics.forlineinself.file:
yieldlineThis passes all tests for me in both Linux and Windows. |
eryksun
commented
Sep 25, 2022
As I mentioned previously, the exception handler in except:
ifnameisnotNoneandnot (
_os.name=='nt'anddeleteanddelete_on_close):
_os.unlink(name)
raise |
modified: Lib/tempfile.py
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…eryksun Files changed: Lib/test/test_tempfile.py Co-authored-by: Eryk Sun <eryksun@gmail.com>
Ev2geny
commented
Oct 2, 2022
@eryksun , can you please have a look at this discussion about the fact, that the directory entry is not unlinked on Unix. I think you a correct person to have an idea there. |
modified: library/tempfile.rst
…into fix-issue-14243
@zooba , I have done my best to implement your comments and also got a help of eryksun. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
zooba
commented
Oct 4, 2022
Thanks for all your hard work on this! I tweaked some of the docs slightly, and will merge once CI finishes again. |
…ryFile on Windows and Python 3.12. delete_on_close is available in Python 3.12: - python/cpython#58451 - python/cpython#97015 so we don't need a custom NamedTemporaryFile implementation anymore.
The temporary file finalizer's check that the file is not already closed was causing the upload to be skipped if the stream was used as a context manager without an explicit call to .close(), because CPython's implementation of .__exit__() for NamedTemporaryFile objects closes the underlying file before calling the wrapper's own .close() method. After changing to `weakref.finalize` in commit 3fd7f1e, uploading temporary files became inherently idempotent, so the check that the file is not already closed is no longer necessary anyway. This change deletes that check. Furthermore, changes to the NamedTemporaryFile implementation with the addition of its delete_on_close parameter in Python 3.12 (python/cpython#97015) make its .__exit__() method no longer call its .close() method at all, so this change also embeds the finalizer as a hook in a wrapper around both .close() and .__exit__() separately.
Remove temp files manually for OS portablity in python 3.10+ `delete_on_close` for temp files is added in [3.12](python/cpython#97015) Signed-off-by: Jeffrey Martin <jemartin@nvidia.com>
Remove temp files manually for OS portablity in python 3.10+ `delete_on_close` for temp files is added in [3.12](python/cpython#97015) Signed-off-by: Jeffrey Martin <jemartin@nvidia.com>
Remove temp files manually for OS portablity in python 3.10+ `delete_on_close` for temp files is added in [3.12](python/cpython#97015) Signed-off-by: Jeffrey Martin <jemartin@nvidia.com>
* align path access for OS agnostic support * rely on os.sep when call in `split()` on path string * lean in on Path `/` operator when combining with a Path object Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * retrieve executable from runtime * avoid hard coded executbale path by asking runtime path * guard file removal during cleanup Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * force report digest output as `utf-8` Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * test updates for windows temp files Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * set encoding for all file operations without `binary` flag Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * portable temp file remove for 3.10+ Remove temp files manually for OS portablity in python 3.10+ `delete_on_close` for temp files is added in [3.12](python/cpython#97015) Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * add windows pytest Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * update ggml temp file Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * variable name precision Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> * manual windows testing only Signed-off-by: Jeffrey Martin <jemartin@nvidia.com> --------- Signed-off-by: Jeffrey Martin <jemartin@nvidia.com>
This is a reincarnation of the already marked as awaiting mergePR 22431, which was subsequently closed due to some git mistake.
This PR fixes issue 58451: tempfile.NamedTemporaryFile not particularly useful on Windows
tempfile.NamedTemporaryFileis too hard to use portably when you need to open the file by name after writing it. To do that, you need to close the file first (on Windows), which means you have to passdelete=False, which in turn means that you get no help in cleaning up the actual file resource,Hence at the moment there is no out of the box solution to use tempfile.NamedTemporaryFile on Windows in such scenario (which is often used in unit testing):
In this Pull Request the issue is solved by adding an additional optional argument to
NamedTemporaryFile() 'delete_on_close'(default is True). It works in combination with already existing argument'delete', and determines the deletion behaviour.If delete is true (the default) and delete_on_close is true (the default), the file is deleted as soon as it is closed. If delete is true and delete_on_close is false, the file is deleted on context manager exit only, if no context manager was used, then the file is deleted only when the file-like object is finalized. If delete is false, the value of delete_on_close is ignored.
So, the change shall be fully backwards compatible.