Welcome, guest | Sign In | My Account | Store | Cart

This code was originally designed for dynamically creating HTML. It takes a template, which is a string that may included embedded Python code, and returns another string where any embedded Python is replaced with the results of executing that code.

Python, 147 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#
# replcode.py
# By Joel Gould
#   joelg@alum.mit.edu
#   http://www.gouldhome.com/
#
# This subroutine takes a string, which may have embedded Python code, and
# executes that embedded code, returning the resulting string.  It is useful
# for things like dynamically producing webpages.
#
# We first execute any code in [!! ... !!].  Then we evaluate any code in
# [?? ... ??].  We do allow nested block of executed code.  To use a nested
# block, include an integer in the block delimiters, ex: [1!! ... !!1]
#
# You can pass an errorLogger function to runPythonCode.  This function
# will be called to print additional error messages.  Default is 
# sys.stdout.write().
#
# Use the special function "OUTPUT" inside the embeded Python code to add text
# to the output.
#
# Here is a sample of using replcode:
#
# >>> import replcode
# >>> input_text = """
# ...     Normal line.
# ...     Expression [?? 1+2 ??].
# ...     Global variable [?? variable ??].
# ...     [!!
# ...         def foo(x):
# ...         	return x+x !!].
# ...     Function [?? foo('abc') ??].
# ...     [!!
# ...         OUTPUT('Nested call [?? variable ??]') !!].
# ...     [!!
# ...         OUTPUT('''Double nested [1!!
# ...                       myVariable = '456' !!1][?? myVariable ??]''') !!].
# ... """
# >>> global_dict = { 'variable': '123' }
# >>> output_text = replcode.runPythonCode(input_text,global_dict)
# >>> print output_text
#
#     Normal line.
#     Expression 3.
#     Global variable 123.
#     .
#     Function abcabc.
#     Nested call 123.
#     Double nested 456.
#

import re
import sys
import string

#---------------------------------------------------------------------------

def runPythonCode(data, global_dict={}, local_dict=None, errorLogger=None):
    eval_state = EvalState(global_dict, local_dict, errorLogger)

    data = re.sub(r'(?s)\[(?P<num>\d?)!!(?P<code>.+?)!!(?P=num)\]', eval_state.exec_python, data)
    data = re.sub(r'(?s)\[\?\?(?P<code>.+?)\?\?\]', eval_state.eval_python, data)
    return data

#---------------------------------------------------------------------------
# This class is used to encapuslate the global and local dictionaries with
# the exec_python and eval_python functions.
    
class EvalState:

    def __init__(self, global_dict, local_dict, errorLogger):
        self.global_dict = global_dict
        self.local_dict = local_dict
        if errorLogger:
            self.errorLogger = errorLogger
        else:
            self.errorLogger = sys.stdout.write
        # copy these things into the global dictionary
        self.global_dict['OUTPUT'] = OUTPUT
        self.global_dict['sys'] = sys
        self.global_dict['string'] = string
        self.global_dict['__builtins__'] = __builtins__
        
    # Subroutine called from re module for every block of code to be
    # executed. Executed code can not return anything but it is allowed to
    # call the OUTPUT subroutine.  Any string produced from OUTPUT will
    # added to the OUTPUT_TEXT global variable and returned.

    def exec_python(self, result):
        # Condition the code.  Replace all tabs with four spaces.  Then make
        # sure that we unindent every line by the indentation level of the
        # first line.
        code = result.group('code')
        code = string.replace(code, '\t', '    ')
        result2 = re.search(r'(?P<prefix>\n[ ]*)[#a-zA-Z0-9''"]', code)
        if not result2:
            raise ParsingError,'Invalid template code expression: ' + code
        code = string.replace(code, result2.group('prefix'), '\n')
        code = code + '\n'

        try:
            self.global_dict['OUTPUT_TEXT'] = ''
            if self.local_dict:
                exec code in self.global_dict, self.local_dict 
            else:
                exec code in self.global_dict
            return self.global_dict['OUTPUT_TEXT']
        except:
            self.errorLogger('\n---- Error parsing: ----\n')
            self.errorLogger(code)
            self.errorLogger('\n------------------------\n')
            raise

    # Subroutine called from re module for every block of code to be
    # evaluated. Returned the result of the evaluation (should be a string).

    def eval_python(self, result):
        code = result.group('code')
        code = string.replace(code, '\t', '    ')
        try:
            if self.local_dict:
                result = eval(code, self.global_dict, self.local_dict)
            else:
                result = eval(code, self.global_dict)
            return str(result)
        except:
            self.errorLogger('\n---- Error parsing: ----\n')
            self.errorLogger(code)
            self.errorLogger('\n------------------------\n')
            raise

#---------------------------------------------------------------------------
# This routine is only called when OUTPUT() is included in executed Python
# code from the templates.  It evaluates its parameter as if it was a
# template and appends the result to the OUTPUT_TEXT variable in the global
# dictionary.    

def OUTPUT(data):
    # This magic python code extracts the local and global dictionaries in
    # the stack frame which was in effect when OUTPUT was called.
    try:
        raise ZeroDivisionError
    except ZeroDivisionError:
        local_dict = sys.exc_info()[2].tb_frame.f_back.f_locals
        global_dict  = sys.exc_info()[2].tb_frame.f_back.f_globals

    global_dict['OUTPUT_TEXT'] = global_dict['OUTPUT_TEXT'] +         runPythonCode(data, global_dict, local_dict)

I originally designed this code to build my home page. Since that I have use this same code for a CGI-based website and for a documentation generation program.

Usually the input string is taken directly from a file and the output string is written to another file. Although when using CGI, the output string can be written directly to stdout.

By passing in a dictionary, you control the global namespace in which the embedded Python code is run. If you want to share variables with the embedded Python code, add those variables to the global dictionary before calling runPythonCode().

When an uncaught exception is raised in the embedded code, a dump of the code being evaluated is first written to stdout (or to errorLogger.write() if specified) before the exception is passed on to the routine that called runPythonCode().

There are two different types of code blocks. Code inside [?? ??] is evaluated. That code should return a string, which will be used to replace the embedded Python code. Code inside [!! !!] is executed. That code is not expected to return anything. However, you can call OUTPUT from inside executed code to specify text that should replace the executed Python code. This makes it possible to use, for example, loops to generate multiple blocks of output text.

1 comment

amiber chen 11 years, 5 months ago  # | flag

very good ,the codes can use to create a new language based python