Leaving files unchanged

Many build systems, e.g. make, CMake and Sphinx, look at file modification times to decide whether to rebuild or not. If the file is written by some earlier step, then it always appears as modified and a spurious rebuild happens.

Here is a small replacement for open that only touches the file if the content changes:

import StringIO
import os

class QOpen(StringIO.StringIO):
    def __init__(self, *args):
        self.__args = args
        StringIO.StringIO.__init__(self)

    def close(self):
        import StringIO, os
        fname = self.__args[0]
        if not os.access(fname, os.R_OK) or self.getvalue() != open(fname).read():
            open(*self.__args).write(self.getvalue())
        StringIO.StringIO.close(self)

    def __del__(self):
        if not self.closed:
            self.close()

In use:

>>> from qopen import QOpen
>>> f = QOpen("foo", "w")
>>> print >>f, "Hello world"

The magic happens in the close method. It reads the existing file, and only writes the output if the file differs. Because of this, file errors only appear when the file is closed:

>>> from qopen import QOpen
>>> f = QOpen("/not_allowed", "w")
>>> print >>f, "Hi there!"
>>> f.close()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "qopen.py", line 13, in close
    open(*self.__args).write(self.getvalue())
IOError: [Errno 13] Permission denied: '/not_allowed'