"""String interpolation 20 Oct 1996 Initial version called Itpl by Ka-Ping Yee 13 Jan 2000 Modified by Bastian Kleineidam to emulate Perls Text::Template Usage: Template data consists of normal text and python code fragments. Code fragments start with a dollar sign followed either by 1) variable or attribute reference or subscription or function call evaluates to the str() value of the given expression example: $printit() 2) {python statements} append arbitrary objects to the output list Limitations: 1) Code fragments are arbitrary strings with matching "{}" parantheses. No further syntax verification is done. 2) Parameters to function calls must not have parantheses inside. Parameters to subscriptions must not have brackets inside. So this is not allowed: $func((tuple1,tuple2)) $func(map[key]) But this is allowed: $func(param)[1] $a[x][y][z](blubb) In code fragments, you can write every Python code: ${OUT = OUT + trunk[idx[1](data())]} Example: import Template t = Template.Template("blabla.tmpl") open("blabla","w").write(t.fill_in({"var1":1,"var2":'hui'})) blabla.tmpl: Hello, this is a demo. var1 is $var1, and var2 is $var2. """ import sys, string, re from types import StringType # type argument (we cannot distinguish between filename and string type) FILENAME = 0 ARRAY = 1 FILE = 2 STRING = 3 # regular expressions for lexical analysis # note we only match a subset of the language definition # we match identifiers, attribute reference, subscriptions, # slicings and function calls identifier = "[a-zA-Z_][a-zA-Z0-9_]*" attributeref = identifier + "(\." + identifier + ")*" subscription = "\[.*\]" callargs = "\(.*\)" # now the whole regex regexvar = re.compile("^"+attributeref+"("+subscription+"|"+callargs+")*") class TemplateError(StandardError): pass class Template: def __init__(self, data, argtype = FILENAME): "load the template in self.data" if argtype == FILENAME: file = open(data) self.data = string.join(file.readlines(), "") file.close() elif argtype == FILE: if data.closed: wasclosed = 1 open(data) else: wasclosed = 0 self.data = string.join(data.readlines(), "") if wasclosed: data.close() elif argtype == ARRAY: self.data = string.join(data, "\n") else: self.data = data if type(self.data) != StringType: raise TemplateError, "could not read data" def fill_in(self, dict={}): "parse the template and fill in values" try: namechars = 'abcdefghijklmnopqrstuvwxyz' \ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'; out = "" pos = 0 while pos < len(self.data) and \ string.find(self.data, "$", pos) != -1: dollar = string.find(self.data, "$", pos) # append pure text without dollar out = out + self.data[pos:dollar] pos = dollar + 1 nextchar = self.data[dollar+1] if nextchar == '{': # apply a code fragment level = 1 while level: pos = pos + 1 if self.data[pos] == "{": level = level + 1 elif self.data[pos] == "}": level = level - 1 output = [] exec self.data[(dollar+2):pos] in dict, {"output":output} for obj in output: out = out + obj pos = pos + 1 elif nextchar in namechars: # scan a variable access var = regexvar.match(self.data[pos:]) if var: # Houston, we have a match! # extract the string and evaluate it var = var.group() pos = pos + len(var) out = out + str(eval(var, dict)) else: if nextchar == "$": pos = pos + 1 out = out + "$" if pos < len(self.data): out = out + self.data[pos:] except: sys.stderr.write("Template.py: parse error at pos "+`pos`+":\n"+\ out+"\n") return out