Staging
v0.5.1
https://github.com/python/cpython
Revision b479dc561c1e778c892f04d1e9673400e95d7e78 authored by Guido van Rossum on 05 September 2001, 22:52:50 UTC, committed by Guido van Rossum on 05 September 2001, 22:52:50 UTC
While not even documented, they were clearly part of the C API,
there's no great difficulty to support them, and it has the cool
effect of not requiring any changes to ExtensionClass.c.
1 parent a40c793
Raw File
Tip revision: b479dc561c1e778c892f04d1e9673400e95d7e78 authored by Guido van Rossum on 05 September 2001, 22:52:50 UTC
Add PyMethod_Function(), PyMethod_Self(), PyMethod_Class() back.
Tip revision: b479dc5
compilerlike.py
"""
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
back to top