a script that replaces an old file with a new one only if the contents actually changed if not the new file is simply deleted this avoids wholesale rebuilds when a code regeneration phase does not actually change the intree generated code a context manager for updating a file via a temp file the context manager provides two open files the source file open for reading and the temp file open for writing upon exiting both files are closed and the source file is replaced with the temp file xxx optionally use tempfile temporaryfile now compare a context manager for updating a file via a temp file the context manager provides two open files the source file open for reading and the temp file open for writing upon exiting both files are closed and the source file is replaced with the temp file xxx optionally use tempfile temporaryfile re raise now compare
import contextlib
import os
import os.path
import sys


@contextlib.contextmanager
def updating_file_with_tmpfile(filename, tmpfile=None):
    
    
    if not tmpfile:
        tmpfile = filename + '.tmp'
    elif os.path.isdir(tmpfile):
        tmpfile = os.path.join(tmpfile, filename + '.tmp')

    with open(filename, 'rb') as infile:
        line = infile.readline()

    if line.endswith(b'\r\n'):
        newline = "\r\n"
    elif line.endswith(b'\r'):
        newline = "\r"
    elif line.endswith(b'\n'):
        newline = "\n"
    else:
        raise ValueError(f"unknown end of line: {filename}: {line!a}")

    with open(tmpfile, 'w', newline=newline) as outfile:
        with open(filename) as infile:
            yield infile, outfile
    update_file_with_tmpfile(filename, tmpfile)


def update_file_with_tmpfile(filename, tmpfile, *, create=False):
    try:
        targetfile = open(filename, 'rb')
    except FileNotFoundError:
        if not create:
            raise  
        outcome = 'created'
        os.replace(tmpfile, filename)
    else:
        with targetfile:
            old_contents = targetfile.read()
        with open(tmpfile, 'rb') as f:
            new_contents = f.read()
        
        if old_contents != new_contents:
            outcome = 'updated'
            os.replace(tmpfile, filename)
        else:
            outcome = 'same'
            os.unlink(tmpfile)
    return outcome


if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('--create', action='store_true')
    parser.add_argument('--exitcode', action='store_true')
    parser.add_argument('filename', help='path to be updated')
    parser.add_argument('tmpfile', help='path with new contents')
    args = parser.parse_args()
    kwargs = vars(args)
    setexitcode = kwargs.pop('exitcode')

    outcome = update_file_with_tmpfile(**kwargs)
    if setexitcode:
        if outcome == 'same':
            sys.exit(0)
        elif outcome == 'updated':
            sys.exit(1)
        elif outcome == 'created':
            sys.exit(2)
        else:
            raise NotImplementedError