mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			163 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
compilerlike -- framework code for building compiler-like programs.
 | 
						|
 | 
						|
There is a common `compiler-like' pattern in Unix scripts which is useful
 | 
						|
for translation utilities of all sorts.  A program following this pattern
 | 
						|
behaves as a filter when no argument files are specified on the command
 | 
						|
line, but otherwise transforms each file individually into a corresponding
 | 
						|
output file.
 | 
						|
 | 
						|
This module provides framework and glue code to make such programs
 | 
						|
easy to write.  You supply a function to massage the file data.  It
 | 
						|
always takes initial name and filename arguments; depending on which
 | 
						|
entry point you use, it can also take input and output file pointers,
 | 
						|
or it can take a string consisting of the entire file's data and
 | 
						|
return a replacement, or it can take in succession strings consisting
 | 
						|
of each of the file's lines and return a translated line for each.
 | 
						|
 | 
						|
The fourth, optional argument of each entry point is a name
 | 
						|
transformation function or name suffix string.  If it is of string
 | 
						|
type, the shortest suffix of each filename beginning with the first
 | 
						|
character of the argument string is stripped off.  If the first
 | 
						|
character of the argument does not occur in the filename, no suffix is
 | 
						|
removed.  Then the name suffix argument is concatenated to the end of
 | 
						|
the stripped filename.  (Thus, a name suffix argument of ".x" will
 | 
						|
cause the filenames foo.c and bar.d to be transformed to foo.x and
 | 
						|
bar.x respectively.)
 | 
						|
 | 
						|
Argument files are transformed in left to right order in the argument list.
 | 
						|
A filename consisting of a dash is interpreted as a directive to read from
 | 
						|
standard input (this can be useful in pipelines).
 | 
						|
 | 
						|
Replacement of each file is atomic and doesn't occur until the
 | 
						|
translation of that file has completed.  Any tempfiles are removed
 | 
						|
automatically on any exception thrown by the translation function,
 | 
						|
and the exception is then passed upwards.
 | 
						|
"""
 | 
						|
 | 
						|
# Requires Python 2.
 | 
						|
import sys, os, filecmp, traceback
 | 
						|
 | 
						|
def filefilter(name, arguments, trans_data, trans_filename=None):
 | 
						|
    "Filter stdin to stdout, or file arguments to renamed files."
 | 
						|
    if not arguments:
 | 
						|
        trans_data(name, "stdin", sys.stdin, sys.stdout)
 | 
						|
    else:
 | 
						|
        for file in arguments:
 | 
						|
            if file == '-':             # - is conventional for stdin
 | 
						|
                file = "stdin"
 | 
						|
                infp = sys.stdin
 | 
						|
            else:
 | 
						|
                infp = open(file)
 | 
						|
            tempfile = file + ".~%s-%d~" % (name, os.getpid())
 | 
						|
            outfp = open(tempfile, "w")
 | 
						|
            try:
 | 
						|
                trans_data(name, file, infp, outfp)
 | 
						|
            except:
 | 
						|
                os.remove(tempfile)
 | 
						|
                # Pass the exception upwards
 | 
						|
                (exc_type, exc_value, exc_traceback) = sys.exc_info()
 | 
						|
                raise exc_type, exc_value, exc_traceback
 | 
						|
            if filecmp.cmp(file, tempfile):
 | 
						|
                os.remove(tempfile)
 | 
						|
            else:
 | 
						|
                if not trans_filename:
 | 
						|
                    os.rename(tempfile, file)
 | 
						|
                elif type(trans_filename) == type(""):
 | 
						|
                    i = file.rfind(trans_filename[0])
 | 
						|
                    if i > -1:
 | 
						|
                        file = file[:i]
 | 
						|
                    os.rename(tempfile, stem + trans_filename)
 | 
						|
                else:
 | 
						|
                    os.rename(tempfile, trans_filename(file))
 | 
						|
 | 
						|
def line_by_line(name, file, infp, outfp, translate_line):
 | 
						|
    "Hook to do line-by-line translation for filters."
 | 
						|
    while 1:
 | 
						|
        line = infp.readline()
 | 
						|
        if line == "":
 | 
						|
            break
 | 
						|
        elif line:      # None returns are skipped
 | 
						|
            outfp.write(translate_line(name, file, line))
 | 
						|
 | 
						|
def linefilter(name, arguments, trans_data, trans_filename=None):
 | 
						|
    "Filter framework for line-by-line transformation."
 | 
						|
    return filefilter(name,
 | 
						|
                  arguments,
 | 
						|
                  lambda name, file, infp, outfp: line_by_line(name, file, infp, outfp, trans_data),
 | 
						|
                  trans_filename)
 | 
						|
 | 
						|
def sponge(name, arguments, trans_data, trans_filename=None):
 | 
						|
    "Read input sources entire and transform them in memory."
 | 
						|
    if not arguments:
 | 
						|
        sys.stdout.write(trans_data(name, "stdin", sys.stdin.read()))
 | 
						|
    else:
 | 
						|
        for file in arguments:
 | 
						|
            infp = open(file)
 | 
						|
            indoc = infp.read()
 | 
						|
            infp.close()
 | 
						|
            tempfile = file + ".~%s-%d~" % (name, os.getpid())
 | 
						|
            try:
 | 
						|
                outfp = open(tempfile, "w")
 | 
						|
            except OSError:
 | 
						|
                sys.stderr.write("%s: can't open tempfile" % name)
 | 
						|
                return 1
 | 
						|
            try:
 | 
						|
                outdoc = trans_data(name, file, indoc)
 | 
						|
            except:
 | 
						|
                os.remove(tempfile)
 | 
						|
                # Pass the exception upwards
 | 
						|
                (exc_type, exc_value, exc_traceback) = sys.exc_info()
 | 
						|
                raise exc_type, exc_value, exc_traceback
 | 
						|
            if outdoc == indoc:
 | 
						|
                os.remove(tempfile)
 | 
						|
            else:
 | 
						|
                outfp.write(outdoc)
 | 
						|
                if not trans_filename:
 | 
						|
                    os.rename(tempfile, file)
 | 
						|
                elif type(trans_filename) == type(""):
 | 
						|
                    i = file.rfind(trans_filename[0])
 | 
						|
                    if i > -1:
 | 
						|
                        file = file[:i]
 | 
						|
                    os.rename(tempfile, file + trans_filename)
 | 
						|
                else:
 | 
						|
                    os.rename(tempfile, trans_filename(file))
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    import getopt
 | 
						|
 | 
						|
    def nametrans(name):
 | 
						|
        return name + ".out"
 | 
						|
 | 
						|
    def filefilter_test(name, file, infp, outfp):
 | 
						|
        "Test hook for filefilter entry point -- put dashes before blank lines."
 | 
						|
        while 1:
 | 
						|
            line = infp.readline()
 | 
						|
            if not line:
 | 
						|
                break
 | 
						|
            if line == "\n":
 | 
						|
                outfp.write("------------------------------------------\n")
 | 
						|
            outfp.write(line)
 | 
						|
 | 
						|
    def linefilter_test(name, file, data):
 | 
						|
        "Test hook for linefilter entry point -- wrap lines in brackets."
 | 
						|
        return "<" + data[:-1] + ">\n"
 | 
						|
 | 
						|
    def sponge_test(name, file, data):
 | 
						|
        "Test hook for the sponge entry point -- reverse file lines."
 | 
						|
        lines = data.split("\n")
 | 
						|
        lines.reverse()
 | 
						|
        return "\n".join(lines)
 | 
						|
 | 
						|
    (options, arguments) = getopt.getopt(sys.argv[1:], "fls")
 | 
						|
    for (switch, val) in options:
 | 
						|
        if switch == '-f':
 | 
						|
            filefilter("filefilter_test", arguments, filefilter_test,nametrans)
 | 
						|
        elif switch == '-l':
 | 
						|
            linefilter("linefilter_test", arguments, linefilter_test,nametrans)
 | 
						|
        elif switch == '-s':
 | 
						|
            sponge("sponge_test", arguments, sponge_test, ".foo")
 | 
						|
        else:
 | 
						|
            print "Unknown option."
 | 
						|
 | 
						|
# End
 |