Atomic file save -- code review and comments requested

Steven D'Aprano steve at pearwood.info
Wed Sep 2 20:02:43 CEST 2015


Howdy,

I have a utility function for performing atomic file saves, and I'd like to
ask for a code review and comments.

I have published this on ActiveState:

https://code.activestate.com/recipes/579097-safely-and-atomically-write-to-a-file/

under an MIT licence. You should read the version there, I discuss the
use-case for the function and include an extensive doc string. Feel free to
comment either here or on the ActiveState site.

Here is the function, minus the docstring (for brevity):


import contextlib
import os
import stat
import tempfile

@contextlib.contextmanager
def atomic_write(filename, text=True, keep=True,
                 owner=None, group=None, perms=None,
                 suffix='.bak', prefix='tmp'):
    t = (uid, gid, mod) = (owner, group, perms)
    if any(x is None for x in t):
        info = os.stat(filename)
        if uid is None:
            uid = info.st_uid
        if gid is None:
            gid = info.st_gid
        if mod is None:
            mod = stat.S_IMODE(info.st_mode)
    path = os.path.dirname(filename)
    fd, tmp = tempfile.mkstemp(
                  suffix=suffix, prefix=prefix, dir=path, text=text)
    try:
        with os.fdopen(fd, 'w' if text else 'wb') as f:
            yield f
        os.rename(tmp, filename)
        tmp = None
        os.chown(filename, uid, gid)
        os.chmod(filename, mod)
    finally:
        if (tmp is not None) and (not keep):
            # Silently delete the temporary file. Ignore any errors.
            try:
                os.unlink(tmp)
            except:
                pass




Usage is:

with atomic_write("mydata.txt") as f:
    f.write("some data")
    # if an error occurs in here, mydata.txt is preserved

# if no error occurs and the with-block exits cleanly,
# mydata.txt is atomically overwritten with the new contents.



The function is written for Python 2.6, but should work on 2.7 as well.

I'm looking for a review of the code, and any general comments. In
particular, have I missed any ways that the function may fail and lose
data?

One question comes to mind -- should I perform a flush and/or sync of the
file before the rename?



Thanks in advance for all constructive comments.





-- 
Steven



More information about the Python-list mailing list