mako是一个高性能的python模板库,它的语法和api借鉴了很多其他的模板库,如django、jinja2等等。
基本用法
创建模板并渲染它的最基本的方法是使用 template 类:
from mako.template import templatet = template('hello world!')print t.render()
传给 template 的文本参数被编译为一个python模块。模块包含一个 render_body() 函数,它产生模板的输出。调用 render() 方法时,mako建立了一个模板的运行环境,并调用 render_body() 函数,把输出保存到缓冲,返回它的字符串内容。
render_body() 函数中可以访问一个变量集。可以向 render() 方法发送额外的关键词参数来指定这些变量:
from mako.template import templatet = template('hello, ${name}!')print t.render(name='yeolar')
render() 方法使mako创建一个 context 对象,它存储模板可以访问的所有变量和一个用来保存输出的缓冲。也可以自己创建 context ,用 render_context() 方法使模板用它来渲染:
from mako.template import templatefrom mako.runtime import contextfrom stringio import stringiot = template('hello, ${name}!')buf = stringio()c = context(buf, name='yeolar')t.render_context(c)print buf.getvalue()
使用文件模板
template 也可以从文件加载模板,使用 filename 参数:
from mako.template import templatet = template(filename='/docs/tpl.txt')print t.render()
为了提高性能,从文件加载的 template 还可以在文件系统中将生成的模块缓存为一般的python模块文件(.py文件),这通过添加 module_directory 参数实现:
from mako.template import templatet = template(filename='/docs/tpl.txt', module_directory='/tmp/mako_modules')print t.render()
上面的代码渲染后,会创建一个/tmp/mako_modules/docs/tpl.txt.py文件,其中包含模块的源代码。下次同样参数的 template 创建时,自动重用这个模块文件。
使用templatelookup
到现在的例子都是有关单个 template 对象的用法。如果模板中的代码要定位其他模板资源,需要某种使用uri来找到它们的方法。这种需求是由 templatelookup 类来达到的。这个类通过传入一个模板查找目录的列表来构造,然后作为关键词参数传给 template 对象:
from mako.template import templatefrom mako.lookup import templatelookuplookup = templatelookup(directories=['/docs'])t = template(' hello word!', lookup=lookup)
上面创建的模板中包含文件header.txt。为了查找header.txt,传了一个 templatelookup 对象给它。
通常,应用会以文本文件形式在文件系统上存储大部分或全部的模板。一个真正的应用会直接从 templatelookup 取得它的模板,使用 get_template() 方法,它接受需要的模板的uri作为参数:
from mako.template import templatefrom mako.lookup import templatelookuplookup = templatelookup(directories=['/docs'], module_directory='/tmp/mako_modules')def serve_template(t_name, **kwargs): t = lookup.get_template(t_name) print t.render(**kwargs)
上面的例子中我们创建了一个 templatelookup ,它从/docs目录中查找模板,并把所有的模块文件存储到/tmp/mako_modules目录中。通过将传入的uri附加到每个查找目录来定位模板,如传递/etc/beans/info.txt,将查找文件/docs/etc/beans/info.txt,如果没找到将抛出 toplevelnotfound 异常。
当定位到模板的时候,传给 get_template() 调用的uri也会作为 template 的 uri 属性。 template 使用这个uri来得到模块文件的名字,因此上面的例子中对/etc/beans/info.txt会创建模块文件/tmp/mako_modules/etc/beans/info.txt.py。
设置收集的大小
templatelookup 还满足将内存中缓存的模板总数设为一个固定的值。默认情况 templatelookup 大小是不限的。可以用 collection_size 参数指定一个固定值:
lookup = templatelookup(directories=['/docs'], module_directory='/tmp/mako_modules', collection_size=500)
上面的 lookup 将模板加载到内存中的上限是500个。之后,它将使用lru策略来清理替换模板。
设置文件系统检查
templatelookup 的另一个重要标志是 filesystem_checks 。默认为 true ,每次 get_template() 方法返回一个模板,会比较原始模板文件的修改时间和模板的最近加载时间,如果文件更新,就重新加载和编译模板。在生产系统中,将 filesystem_checks 设为 false 能获得一些性能的提升。
使用unicode和编码
template 和 templatelookup 可以设置 output_encoding 和 encoding_errors 参数来将输出编码为python支持的编码格式:
from mako.template import templatefrom mako.lookup import templatelookuplookup = templatelookup(directories=['/docs'], output_encoding='utf-8', encoding_errors='replace')t = lookup.get_template('foo.txt')print t.render()
使用python 3时,如果设置了 output_encoding , render() 方法将返回一个 bytes 对象,否则返回 string 。
render_unicode() 方法返回模板输出为python unicode 对象,python 3为 string :
print t.render_unicode()
上面的方法没有输出编码的参数,可以自行编码:
print t.render_unicode().encode('utf-8', 'replace')
注意mako中模板的底层输出流是python unicode对象。
处理异常
模板异常可能发生在两个地方。一个是当你查找、解析和编译模板的时候,一个是运行模板的时候。模板运行中发生的异常会正常在产生问题的python代码处抛出。mako有自己的一组异常类,它们主要用于模板构造的查找和编译阶段。mako提供了一些库例程用来对异常栈提供mako的信息,并将异常输出为文本或html格式。python文件名、行号和代码片段会被转换为mako模板文件名、行号和代码片段。mako模板模块的行会被转换为原始的模板文件对应行。
text_error_template() 和 html_error_template() 函数用于格式化异常跟踪。它们使用 sys.exc_info() 来得到最近抛出的异常。这些处理器的用法像下面这样:
from mako import exceptionstry: t = lookup.get_template(uri) print t.render()except: print exceptions.text_error_template().render()或者渲染为html:from mako import exceptionstry: t = lookup.get_template(uri) print t.render()except: print exceptions.html_error_template().render()
html_error_template() 模板接受两个选项:指定 full=false 只渲染html的一节,指定 css=false 关闭默认的样式表。如:
print exceptions.html_error_template().render(full=false)
html渲染函数也可以用 format_exceptions 标志加到 template 中。这种情况下,模板在渲染阶段的任何异常在输出中的结果都会替换为 html_error_template() 的输出:
t = template(filename='/foo/bar', format_exceptions=true)print t.render()
注意上面模板的编译阶段发生在构造 template 时,没有定义输出流。因此查找、解析、编译阶段发生的异常正常情况下不会被处理,而是传播下去。渲染前的追溯不包括mako形式的行,这意味着渲染前和渲染中发生的异常会用不同的方式处理,因此 try/except 可能更常用。
错误模板函数使用的底层对象是 richtraceback 对象。这个对象也可以直接用来提供自定义的错误视图。下面是一个用法的样例:
from mako.exceptions import richtracebacktry: t = lookup.get_template(uri) print t.render()except: traceback = richtraceback() for (filename, lineno, function, line) in traceback.traceback: print 'file %s, line %s, in %s' % (filename, lineno, function) print line, '\n' print '%s: %s' % (str(traceback.error.__class__.__name__), traceback.error)
集成mako
在django中集成mako
通过django的中间件可以集成mako。首先需要安装django-mako模块。
在django项目的settings.py文件中,修改 middleware_classes ,添加 djangomako.middleware.makomiddleware 。使用 render_to_response() 函数即可使用:
from djangomako.shortcuts import render_to_responsedef hello_view(request): return render_to_response('hello.txt', {'name': 'yeolar'})
在tornado中集成mako
在tornado中可以直接使用mako,下面是一个使用示例:
import tornado.webimport mako.lookupimport mako.templatelook_up = mako.lookup.templatelookup( directories=[template_path], module_directory='/tmp/mako', output_encoding='utf-8', encoding_errors='replace')class basehandler(tornado.web.requesthandler): def initialize(self, lookup=look_up): '''set template lookup object, defalut is look_up''' self._lookup = lookup def render_string(self, filename, **kwargs): '''override render_string to use mako template. like tornado render_string method, this method also pass request handler environment to template engine. ''' try: template = self._lookup.get_template(filename) env_kwargs = dict( handler = self, request = self.request, current_user = self.current_user, locale = self.locale, _ = self.locale.translate, static_url = self.static_url, xsrf_form_html = self.xsrf_form_html, reverse_url = self.application.reverse_url, ) env_kwargs.update(kwargs) return template.render(**env_kwargs) except: # exception handler pass def render(self, filename, **kwargs): self.finish(self.render_string(filename, **kwargs))