#!/usr/bin/env python3 import os import tempfile def test_atomic_file_replace(): # an example of SAFE atomic file replacement in modern UNIX-like FS # This sample is incomplete since does not call fsync() on a directory. # so, in case of power outage, old file (with old contents) may be # visible in dir with tempfile.TemporaryDirectory() as testdir: # just create sample file with initial contents with open(os.path.join(testdir, 'xxx.dat'), 'wb') as sample: sample.write(b'foo') target = sample.name # if testdir is symlink, we should create temporary file in dir # where pointed file placed, instead of dir where symlink itself # is placed realdir = os.path.dirname(os.path.realpath(target)) with tempfile.NamedTemporaryFile(dir=realdir, mode='wb') as replacement: replacement.write(b'bar') # To prevent data loss in case of power outage # we should first flush data to disk. replacement.flush() os.fdatasync(replacement.fileno()) # rename atomically. If exception happen here, temporary file # will be removed and original file will be left intact os.rename(replacement.name, target) replacement.delete = False assert not os.path.exists(replacement.name) # open replaced file and check that it has been actually replaced with open(target, 'rb') as two: assert two.read() == b'bar' test_atomic_file_replace()