faqts : Computers : Programming : Languages : Python : Snippets

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

14 of 14 people (100%) answered Yes
Recently 10 of 10 people (100%) answered Yes

Entry

Preprocessor (with Python as macro language)

Jul 5th, 2000 10:00
Nathan Wallace, Hans Nowak, Snippet 113, Glyn Webster


"""
Packages: miscellaneous;text
"""
""" A preprocessor in Python that uses Python as it's macro language.
    Python source code fragments between "<* *>" brackets in the text
    will be removed, evaluated and replaced with their output.
    When the source code fragment is an expression its 'output' is the
    value it returns.  When the source code fragment is a sequence of
    statements its 'output' is everything it tries to print to the
    standard output with "print" statements.
    All code fragments are executed in the same namespace, So you can
    define variables or import Python modules in one code fragment,
    then use them in subsequent code fragments.
    Only matching sets of <* *> brackets are processed.  And you can't
    use the strings '<*' '*>' in the text or the code.  That isn't too
    big a problem because they're not valid code in either Python or
    HTML. You can use the code fragment "<* '<' + '*' *>" to
    represent "<*" if you need to.
    Here's a little example of Pymacro in use as a Web page preprocessor
    from the command line. To create and HTML file from the file below
    you'd run the command  "python pymacro.py <gourds.pyml >gourds.html":
        <html>
        <*
        Gourds = 'http://daphne.palomar.edu/wayne/ww0503.htm'
        BodyTags = 'background="board.gif"'
        def Url(s): return '<a href="%s">%s</a>' % (s, s)
        from time import ctime, time
        *>
          <head>
            <title>Links Page</title>
            <meta name="DESCRIPTION" content="Link to Wayne's Gourds page">
          </head>
          <body <*BodyTags*> >
            <b>Here's a link:</b><br>
            <ul>
              <li> Go to The World of Gourds <*Url(Gourds)*>
            </ul>
          </body>
          <small>Last updated: <*ctime(time())*> </small>
        </html>
    Glyn Webster <gdw@cs.waikato.ac.nz> 2020-10-20
"""
import sys, string, cStringIO
def process_to_string(text, namespace = {}):
    """ Replaces all the Python code fragments in the string 'text'
        with their output, as described above, and returns the resulting
        string.
        All the code fragments are executed in 'namespace' (see
        the 'execute' function below). The namespace is empty by
        default. You can supply a preloaded one if you feel like it:
        e.g.
           rough_result = process(heavy_math_stuff, {'pi': 3.14})
           my_namespace = {}
           exec "from math import *" in my_namespace
           precise_result = process(heavy_math_stuff, my_namespace)
    """
    output = cStringIO.StringIO()
    process_to_file(text, output, namespace)
    return ouput.getvalue()
def process_to_file(text, output_file, namespace = {}):
    """ Replaces all the Python code fragments in the string 'text'
        with their output, as described above, writing processed
        text to the file `output' as it goes.
        (This one is faster if you are writing to a file.)
    """
    here = 0
    while 1:
        start = string.find(text, '<*', here)
        end = string.find(text, '*>', start)
        if start == -1 or end == -1 :
            break
        output_file.write(text[here:start])
        output_file.write(execute(text[start+2:end], namespace))
        here = end + 2
    output_file.write(text[here:])
def execute( code, namespace ):
    """ This function executes some Python code in a namespace and
        returns the code's output as a string.
        If the code is an expression its 'output' is the value it returns.
        'Execute' attempts to execute the code as an exppression first,
        and if that causes a syntax error, tries to execute it as a
        sequence of statements. (I can't see any more graceful way to
        tell the difference, sorry.)
        The code may modify the namespace, but that's good, you want
        that to happen, for example you may want to define variables or
        import Python modules into the namespace for subsequent code
        fragments to use.
    """
    try:
        value = eval(code, namespace)
        if value is None:
          return ""
        else:
          return str(value)
    except SyntaxError:
        code = string.strip(code)
        exec code in namespace
        return ""
#   If pymacro.py is called from the command line,
#   process the standard input to the standard output:
#
if __name__ == "__main__":
    process_to_file(sys.stdin.read(), sys.stdout)