#!/usr/bin/python """ Process Template Files (based on Yet Another Python Templating Utility by Alex Martelli) """ # Copyright (C) 2004 Henning Jacobs # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # $Id: TemplateProcessor.py 82 2004-07-11 13:01:44Z henning $ import re, sys import types class _nevermatch: def match(self, line): return None def sub(self, repl, line): return line _never = _nevermatch() def identity(string, why=None): return string def nohandle(string, kind): import traceback traceback.print_exc() return "*** Exception raised in %s '%s'\n" % (kind, string) class TemplateProcessor: def __init__(self, postproc=identity, preproc=identity, handle=nohandle): self.reexpr = re.compile('<\?(=|!|#)([^?]+)\?>') self.restart = re.compile('\s*<\?([^}][^?]+){\?>') self.reend = re.compile('\s*<\?}\?>') self.recont = re.compile('\s*<\?}([^{]+){\?>') self.locals = {'_pb':self.process_block} self.postproc = postproc self.preproc = preproc self.handle = handle def process(self, block, outfunc, globals={}): self.outfunc = outfunc self.globals = globals self.locals['_bl'] = block self.process_block() def process_block(self, i=0, last=None): "Process lines [i, last) of block" def repl(match, self=self): cmd = match.group(1) if cmd == '#': # Comment return '' else: expr = self.preproc(match.group(2), 'eval') try: ret = eval(expr, self.globals, self.locals) except: return self.postproc(self.handle(expr, 'eval')) if cmd == '=': # Ensure that our Expression is a String: if not isinstance(ret, types.StringTypes): ret = str(ret) return self.postproc(ret) else: # ! return '' block = self.locals['_bl'] if last is None: last=len(block) while i < last: line = block[i] match = self.restart.search(line) if match: # a statement starts 'here' # i is the last line NOT to process stat = match.group(1)+':' j = i+1 nest = 1 while j < last: line = block[j] if self.reend.match(line): nest += -1 if nest == 0: break elif self.restart.match(line): nest += 1 elif nest == 1: match = self.recont.match(line) if match: # found a continued statement nestat = match.group(1)+':' stat = '%s _pb(%s,%s)\n%s' % (stat, i+1, j, nestat) i = j # i is the last line NOT to process j += 1 stat = self.preproc(stat, 'exec') stat = '%s _pb(%s,%s)' % (stat, i+1, j) try: exec stat in self.globals, self.locals except: self.outfunc(self.postproc(self.handle(stat, 'exec'))) i = j+1 else: self.outfunc(self.reexpr.sub(repl, line)) i += 1 if __name__ == "__main__": dummytemplate = """ a: , b: , foo: A Comment (invisible): A will be changed now! a: , b: , foo: Hallo? i: Zwei Drei Sonst Continues, but here is the END. """.splitlines(True) globals = {} def changeA(globals=globals): globals['a'] = 2004 def foo(): return 'foo returned by foo() function' globals.update({'Name':'A Dummy Template', 'a':42, 'b':'\xdcmlaute', 'changeA':changeA, 'foo':foo}) proc = TemplateProcessor() proc.process(dummytemplate, sys.stdout.write, globals)