mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	This also includes some cleanup in preparation for a PR to make the "make all" output less noisy. https://bugs.python.org/issue45020
		
			
				
	
	
		
			92 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			92 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
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 (re)generation phase does not
 | 
						|
actually change the in-tree generated code.
 | 
						|
"""
 | 
						|
 | 
						|
import contextlib
 | 
						|
import os
 | 
						|
import os.path
 | 
						|
import sys
 | 
						|
 | 
						|
 | 
						|
@contextlib.contextmanager
 | 
						|
def updating_file_with_tmpfile(filename, tmpfile=None):
 | 
						|
    """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?
 | 
						|
    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  # re-raise
 | 
						|
        outcome = 'created'
 | 
						|
        os.replace(tmpfile, filename)
 | 
						|
    else:
 | 
						|
        with targetfile:
 | 
						|
            old_contents = targetfile.read()
 | 
						|
        with open(tmpfile, 'rb') as f:
 | 
						|
            new_contents = f.read()
 | 
						|
        # Now compare!
 | 
						|
        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
 |