I want to do the following. Create a temporary file and work on it. If successful, rename it to a real file. If it fails, then delete the file.
I tried the following:
with tempfile.NamedTemporaryFile(dir=os.getcwd()) as f:
f.write(b'foobar')
f.delete = False
f.flush()
os.rename(f.name, 'foobar')
However, I still get an exception that the file does not exist when it tries to delete it. I might use a try-catch to ignore this error, but that would be ugly and might ignore other errors too. Or I could use mkstemp()
and manage deletion myself, but that has a problem that it returns a file descriptor instead of a file object, and I couldn't find a way to create a file object from the file descriptor.
Is there any proper solution to the problem?
You need to pass delete=False
during initialization, setting f.delete = False
later is not working. I was able to rename it after writing with this code:
with tempfile.NamedTemporaryFile(dir=os.getcwd(), delete=False) as f:
f.write(b'foobar')
f.flush()
os.rename(f.name, 'foobar')
I went through the code of Lib/tempfile.py to confirm this behavior.
When you call NamedTemporartyFile
, it initializes and returns an instance of _TemporaryFileWrapper
class defined in the same file. Look at the __init__()
of this class:
def __init__(self, file, name, delete=True):
self.file = file
self.name = name
self.delete = delete
self._closer = _TemporaryFileCloser(file, name, delete)
And here is the __init__()
of _TemporaryFileCloser
class:
def __init__(self, file, name, delete=True):
self.file = file
self.name = name
self.delete = delete
When you exit from the with statement, during closing of the file, the self.delete
attribute of _TemporaryFileCloser
is checked. While when you do f.delete = False
, you change the self.delete
attribute of the _TemporaryFileWrapper
class instead of the _TemporaryFileCloser
class. Therefore, the closing behavior is not altered even if it looks like that on developer side. So you must pass delete=False
during the initialization in the with
statement like in my code example.
If you still want to modify the behavior, you can access self._closer
attribute, it will be a little ugly however.
with tempfile.NamedTemporaryFile(dir=os.getcwd()) as f:
f.write(b'foobar')
f.flush()
f._closer.delete = False
os.rename(f.name, 'foobar')
Btw, I performed this on Ubuntu WSL on Windows 10. I'm not sure which OS you are using, that might affect the behavior.