import re class SimpleTemplate(object): re_block = re.compile(r'^\s*%\s*((if|elif|else|try|except|finally|for|while|with).*:)\s*$') re_end = re.compile(r'^\s*%\s*end(.*?)\s*$') re_code = re.compile(r'^\s*%\s*(.*?)\s*$') re_inc = re.compile(r'\{\{(.*?)\}\}') def __init__(self, template): self.code = "\n".join(self.compile(template)) self.co = compile(self.code, '<string>', 'exec') def render(self, **args): ''' Returns the rendered template using keyword arguments as local variables. ''' args['stdout'] = [] args['__builtins__'] = __builtins__ eval(self.co, {}, args) # 執行Python代碼 return ''.join(args['stdout']) def compile(self, template): ''' 將模板轉換爲可執行的Python代碼 ''' def code_str(level, line, value): # 可直接輸出的字符串 value = "".join(value) value = value.replace("'","\'").replace('\\','\\\\') return ' '*level + "stdout.append(r'''%s''')" % value def code_print(level, line, value): # {{...}}中的內容,須要執行代碼後取得字符串再輸出 return ' '*level + "stdout.append(str(%s)) # Line: %d" % (value.strip(), line) def code_raw(level, line, value): # 以%開頭,須要做爲代碼執行 return ' '*level + value.strip() + ' # Line: %d' % line level = 0 # 縮進深度 ln = 0 # 行號(無具體做用,方便定位問題) sbuffer = [] # 模板中的普通字符串 for line in template.splitlines(True): ln += 1 # Line with block starting code m = self.re_block.match(line) if m: if sbuffer: yield code_str(level, ln, sbuffer) sbuffer = [] if m.group(2).strip().lower() in ('elif','else','except','finally'): if level == 0: raise TemplateError('Unexpected end of block in line %d' % ln) level -= 1 yield code_raw(level, ln, m.group(1).strip()) level += 1 continue # Line with % end marker m = self.re_end.match(line) if m: if sbuffer: yield code_str(level, ln, sbuffer) sbuffer = [] if level == 0: raise TemplateError('Unexpected end of block in line %d' % ln) level -= 1 continue # Line with % marker m = self.re_code.match(line) if m: yield code_raw(level, ln, m.group(1).strip()) continue # Line with inline code lasts = 0 for m in self.re_inc.finditer(line): sbuffer.append(line[lasts:m.start(0)]) yield code_str(level, ln, sbuffer) sbuffer = [] lasts = m.end(0) yield code_print(level, ln, m.group(1)) if lasts: sbuffer.append(line[lasts:]) continue # Stupid line sbuffer.append(line) if sbuffer: yield code_str(level, ln, sbuffer) if __name__ == "__main__": # 簡單替換 t1 = SimpleTemplate('Hello {{name}}!') print t1.render(name='<b>World</b>') # 嵌入python語句(必須返回str) t2 = SimpleTemplate('Hello {{name.title() if name else "stranger"}}!') print t2.render(name=None) print t2.render(name="tuzkee") # %執行Python語句 tmp3=""" %if name: Hi <b>{{name}}</b> %else: <i>Hello stranger</i> %end""" t3 = SimpleTemplate(tmp3) print t3.render(name=None) tmp4=""" %for i in range(3): %for j in range(3): {{"%s:%s"%(i,j)}} %end %end""" t4 = SimpleTemplate(tmp4) print t4.render()