用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']())

 

posted @ 2014-12-29 13:14  媛媛画  阅读(955)  评论(0编辑  收藏  举报