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.