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"})