用python写自定义模板
模板语法有点像php
!/usr/bin/env python """ #demo.py.html <html> <?py include head.py.html ?> <body> <?py extend test_for.py.html ?> <div> <pre> test if </pre> <?py if 2 > 1 ?> <p>Yes, it is</p> <?py else ?> <p>No it is not</p> <?py #endif ?> </div> <div> <pre> test def </pre> <?py def p() ?> <?py return 'hello' ?> <?py #enddef ?> Wow! ${p()} </div> </body> </html> #head.py.html <head> <title>Demo Py Template</title> </head> #test_for.py.html <div> <pre> test for <pre> <?py for _ in ['a', 'b', 'c'] ?> Hello ${_} #{_.upper()} <?py #endfor ?> </div> """ from __future__ import absolute_import, division, print_function, with_statement import re import os.path class TemplateLoader(object): pass class DictLoader(object): pass class Template(object): #GLOBALS = {} def __init__(self, tempalte_path): self.tempalte_path = tempalte_path self.globals = { 't': lambda x: x, 'xml_escape': xml_escape, 'html_escape': html_escape, 'e': escape } self.templates = { } def render(self, path, **kwrags): # the stemplae system local never be override _g = kwargs.update(self.globals) code = self.genarate(path, g) exec code in _g return _g['_tt_render']() def genarate(self, path, g): if path in self.templates: return self.templates[path](g) tempalte_path = os.path.join(self.tempalte_path, path) tempalte_func = TemplateParser(tempalte_path).compile() self.templates[path] = tempalte_func return tempalte_func(g) class TemplateParser(object): PY_TOKEN = re.compile(r'<\?py\s*((?:[^=0-9]).*?)\s*\?>') PY_VAR_TOKEN = re.compile(r'(?:[#$])\{(.*?)\}') def __init__(self, path2template, indent=None, include=False): self.path2template = path2template self.indent = indent or 1 self.buffer = '' self._include = include if not self._include: self.buffer += 'def _tt_render():\n' self.puts('_buffer=[]') self.puts('_append=_buffer.append') def puts(self, line): self.buffer += self.indents + "%s" % (line) + '\n' def compile(self): code = self.parse() code = compile(code, '<string>', 'exec', dont_inherit=True) return code def parse(self): f = open(self.path2template) lineno = 1 while True: line = f.readline() if not line: break self.parse_line(line, lineno) lineno += 1 if not self._include: self.puts('return "".join(_buffer)') f.close() return self.buffer def parse_line(self, line, lineno): # remove the whitespace line and comment line if not line.strip() or line.lstrip().startswith('#'): return m = self.PY_TOKEN.search(line) if m: t = m.group(1) parts = t.split(' ', 1) if parts[0] in ('from', 'import'): self.stmt(t) return if parts[0] == 'include': self.include(parts[1]) return if parts[0] == 'extend': self.extend(parts[1]) return if parts[0] in ('for', 'if', 'with', 'def', 'class', 'try'): self.contoll(t) self.indent += 1 return if parts[0] in ('else', 'elif', 'except'): self.indent -= 1 self.contoll(t) self.indent += 1 return if parts[0] in ('#end', '#endfor', '#endif', '#endtry', '#endclass', '#enddef', '#endwith'): self.indent -= 1 return self.stmt(t) return # handle var token ms = self.PY_VAR_TOKEN.finditer(line) if ms: a = None end = 0 start = 0 l = '' for m in ms: t = m.group(1) start = m.start() b = line[end:m.start()] start = m.start() end = m.end() a = line[end:] if l: l += "+ '%s'" % (b) + " + str(%s) " % (t) else: l = "'%s'" % (b) + " + str(%s) " % (t) if a and start: l += "+" + " %r" % (a) if l: self.stmt("_append(" + l + ")") return self.text(line) def text(self, content): content = self.indents + '_append(' + "%r" % (content) + ')\n' self.buffer += content def e(self, formator, t): self.puts('_append("' + formator + '"' + ' % ' + t + ")") def r(self): return '%r' def contoll(self, line): self.buffer += self.indents + line + ':\n' def stmt(self, stmt): self.buffer += self.indents + stmt + ' \n' @property def indents(self): return ' ' * self.indent def include(self, template): p = TemplateParser(template, include=True) self.buffer += p.parse() def extend(self, template): p = TemplateParser(template, self.indent, include=True) self.buffer += p.parse() if __name__ == '__main__': t = TemplateParser('demo.py.html') code = t.compile() ns = {} exec code in ns # print ns print(ns['_tt_render']())