import os import time class Template(object): """A very simple html play templating system. An html file with embedded template statements is converted into a compiled python function which is executed to generate the template's output.""" __slots__ = ("htmlFile", "compileTime", "template") def __init__(self, htmlFile): """Initialize a Template object. htmlFile - the name of the file containing the html""" self.htmlFile = htmlFile self.template = None self.compileTime = 0 def execute(self, vars): """Execute the template using vars to supply variable values. Vars must be a dictionary. Each variable that is referenced in the template must have an entry in the dictionary.""" return self._getTemplate()(vars) def _getTemplate(self): """Return the function that implements this template. The template is regenerated if the file it is created from has been modified since the template was last generated.""" if not self._isUpToDate(): self.compileTime = int(time.time()) self.template = self._compile(self._generate()) return self.template def _isUpToDate(self): """Return true if the template is up to date.""" return self.template and \ (os.stat(self.htmlFile).st_mtime < self.compileTime) def _generate(self): """Return the source code for the function implementing the template.""" f = file(self.htmlFile, "r") lines = f.readlines() f.close(); g = self.Generator(lines) return g.generate() def _compile(self, s): """Compile the source s and return the function object that results.""" c = compile(s, "<string>", "exec") g = {} exec c in g return g["execute"] class Generator(object): """Generate the source code for a template.""" __slots__ = ('lines', 'prefix', 'lookingForStart', 's') def __init__(self, lines): """Initialize a Generator instance. lines - the lines of html source""" self.lines = lines self.lines.reverse() self.prefix = "s += " self.lookingForStart = 1 self.s = "def execute(v):\n s = \"\"\n" def generate(self): """Generate the source code for a template.""" while len(self.lines) > 0: l = self.lines.pop() if self.lookingForStart: self._lookForStart(l) else: self._lookForEnd(l) self.s += " return s\n" self.s += "\n" print self.s return self.s def _lookForStart(self, l): """Look for the start of template code marker <# in l""" i = l.find("<#") if i == -1: self._generateHTML(l, "\\n") else: self._generateHTML(l[:i]) self.lookingForStart = 0 if l[i+2] == '=': self.prefix = "s += " self.lines.append(l[i+3:]) else: self.prefix = "" self.lines.append(l[i+2:]) def _lookForEnd(self, l): """Look for the end of template code marker #> in l""" i = l.find("#>") if i == -1: self._generatePython(l); else: self._generatePython(l[:i]) self.lookingForStart = 1 self.prefix = "s +=" self.lines.append(l[i+2:]) def _generateHTML(self, x, newline=""): """Create code to add x to the template as html.""" self.s += " %s\"%s%s\"\n" % \ (self.prefix, x.rstrip().replace("\"","\\\""), newline) def _generatePython(self, x): """Generate code to add x to the template as python code.""" self.s += " %s%s\n" % \ (self.prefix, x.strip()) if __name__ == "__main__": t = Template("test.html") print t.execute({"v1":"one", "v2":"two"}) print t.execute({"v1":"XXS", "v2":"two"})