This context manager ensures that a file's content is either replaced atomically with new contents or left unchanged. An exception or other error will not leave it empty or with partial content.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from contextlib import contextmanager
import os, binascii
@contextmanager
def replacefile(name):
tmpname = '%s.tmp-%s' % (name, binascii.hexlify(os.urandom(10)).decode('ascii'))
try:
with open(tmpname, 'w+') as f:
yield f
os.rename(tmpname, name)
finally:
try:
os.unlink(tmpname)
except OSError:
pass
|
Using this context manager is similar to using open() in a 'with' statement:
with replacefile(filename) as f:
f.write(...)
You can safely read and write the same file name to process it in-place:
with open(filename) as fin, replacefile(filename) as fout:
fout.write(something(fin.read()))
This context manager depends on POSIX semantics for atomically replacing a file with rename(2). Do not use this on Windows.