原子写入Python文件

我使用Python在一次操作中将文本块写入文件:

open(file, 'w').write(text)

如果脚本被中断,所以文件写入不完成我想没有文件,而不是部分完整的文件。 这可以做到吗?


将数据写入临时文件,并且当数据成功写入时,将文件重命名为正确的目标文件,例如

f = open(tmpFile, 'w')
f.write(text)
# make sure that all data is on disk
# see http://stackoverflow.com/questions/7433057/is-rename-without-fsync-safe
f.flush()
os.fsync(f.fileno()) 
f.close()

os.rename(tmpFile, myFile)

根据doc http://docs.python.org/library/os.html#os.rename

如果成功,重命名将是一个原子操作(这是POSIX要求)。 在Windows上,如果dst已经存在,即使它是一个文件,也会引发OSError; 当dst命名现有文件时,可能无法实现原子重命名

如果src和dst位于不同的文件系统上,该操作可能会在某些Unix版本上失败。

注意:

  • 如果src和dest位置不在同一个文件系统上,它可能不是原子操作

  • 如果在电源故障,系统崩溃等情况下性能/响应比数据完整性更重要, os.fsync步骤可能会被忽略


  • 一个使用Python tempfile实现原子写入的简单片段。

    with open_atomic('test.txt', 'w') as f:
        f.write("huzza")
    

    甚至可以读写同一个文件:

    with open('test.txt', 'r') as src:
        with open_atomic('test.txt', 'w') as dst:
            for line in src:
                f.write(line)
    

    用两个简单的上下文管理器

    import os
    import tempfile as tmp
    from contextlib import contextmanager
    
    @contextmanager
    def tempfile(suffix='', dir=None):
        """ Context for temporary file.
    
        Will find a free temporary filename upon entering
        and will try to delete the file on leaving, even in case of an exception.
    
        Parameters
        ----------
        suffix : string
            optional file suffix
        dir : string
            optional directory to save temporary file in
        """
    
        tf = tmp.NamedTemporaryFile(delete=False, suffix=suffix, dir=dir)
        tf.file.close()
        try:
            yield tf.name
        finally:
            try:
                os.remove(tf.name)
            except OSError as e:
                if e.errno == 2:
                    pass
                else:
                    raise
    
    @contextmanager
    def open_atomic(filepath, *args, **kwargs):
        """ Open temporary file object that atomically moves to destination upon
        exiting.
    
        Allows reading and writing to and from the same filename.
    
        The file will not be moved to destination in case of an exception.
    
        Parameters
        ----------
        filepath : string
            the file path to be opened
        fsync : bool
            whether to force write the file to disk
        *args : mixed
            Any valid arguments for :code:`open`
        **kwargs : mixed
            Any valid keyword arguments for :code:`open`
        """
        fsync = kwargs.get('fsync', False)
    
        with tempfile(dir=os.path.dirname(os.path.abspath(filepath))) as tmppath:
            with open(tmppath, *args, **kwargs) as file:
                try:
                    yield file
                finally:
                    if fsync:
                        file.flush()
                        os.fsync(file.fileno())
            os.rename(tmppath, filepath)
    

    有一个简单的AtomicFile助手:https://github.com/sashka/atomicfile

    链接地址: http://www.djcxy.com/p/42311.html

    上一篇: atomic writing to file with Python

    下一篇: How do I get the path of a the Python script I am running in?