Mako是一個高性能的Python模板庫,它的語法和API借鑒了很多其他的模板庫,如Django、Jinja2等等。
基本用法
創建模板并渲染它的最基本的方法是使用 Template 類:
1
2
3
|
from mako.template import Template t = Template( 'hello world!' ) print t.render() |
傳給 Template 的文本參數被編譯為一個Python模塊。模塊包含一個 render_body() 函數,它產生模板的輸出。調用 render() 方法時,Mako建立了一個模板的運行環境,并調用 render_body() 函數,把輸出保存到緩沖,返回它的字符串內容。
render_body() 函數中可以訪問一個變量集。可以向 render() 方法發送額外的關鍵詞參數來指定這些變量:
1
2
3
|
from mako.template import Template t = Template( 'hello, ${name}!' ) print t.render(name = 'yeolar' ) |
render() 方法使Mako創建一個 Context 對象,它存儲模板可以訪問的所有變量和一個用來保存輸出的緩沖。也可以自己創建 Context ,用 render_context() 方法使模板用它來渲染:
1
2
3
4
5
6
7
8
|
from mako.template import Template from mako.runtime import Context from StringIO import StringIO t = Template( 'hello, ${name}!' ) buf = StringIO() c = Context(buf, name = 'yeolar' ) t.render_context(c) print buf.getValue() |
使用文件模板
Template 也可以從文件加載模板,使用 filename 參數:
1
2
3
|
from mako.template import Template t = Template(filename = '/docs/tpl.txt' ) print t.render() |
為了提高性能,從文件加載的 Template 還可以在文件系統中將生成的模塊緩存為一般的Python模塊文件(.py文件),這通過添加 module_directory 參數實現:
1
2
3
|
from mako.template import Template t = 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 對象:
1
2
3
4
|
from mako.template import Template from mako.lookup import TemplateLookup lookup = TemplateLookup(directories = [ '/docs' ]) t = Template( '<%include file="header.txt" /> hello word!' , lookup = lookup) |
上面創建的模板中包含文件header.txt。為了查找header.txt,傳了一個 TemplateLookup 對象給它。
通常,應用會以文本文件形式在文件系統上存儲大部分或全部的模板。一個真正的應用會直接從 TemplateLookup 取得它的模板,使用 get_template() 方法,它接受需要的模板的URI作為參數:
1
2
3
4
5
6
|
from mako.template import Template from mako.lookup import TemplateLookup lookup = 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 參數指定一個固定值:
1
|
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支持的編碼格式:
1
2
3
4
5
|
from mako.template import Template from mako.lookup import TemplateLookup lookup = 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 :
1
|
print t.render_unicode() |
上面的方法沒有輸出編碼的參數,可以自行編碼:
1
|
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() 來得到最近拋出的異常。這些處理器的用法像下面這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from mako import exceptions try : t = lookup.get_template(uri) print t.render() except : print exceptions.text_error_template().render() 或者渲染為HTML: from mako import exceptions try : t = lookup.get_template(uri) print t.render() except : print exceptions.html_error_template().render() |
html_error_template() 模板接受兩個選項:指定 full=False 只渲染HTML的一節,指定 css=False 關閉默認的樣式表。如:
1
|
print exceptions.html_error_template().render(full = False ) |
HTML渲染函數也可以用 format_exceptions 標志加到 Template 中。這種情況下,模板在渲染階段的任何異常在輸出中的結果都會替換為 html_error_template() 的輸出:
1
2
|
t = Template(filename = '/foo/bar' , format_exceptions = True ) print t.render() |
注意上面模板的編譯階段發生在構造 Template 時,沒有定義輸出流。因此查找、解析、編譯階段發生的異常正常情況下不會被處理,而是傳播下去。渲染前的追溯不包括Mako形式的行,這意味著渲染前和渲染中發生的異常會用不同的方式處理,因此 try/except 可能更常用。
錯誤模板函數使用的底層對象是 RichTraceback 對象。這個對象也可以直接用來提供自定義的錯誤視圖。下面是一個用法的樣例:
1
2
3
4
5
6
7
8
9
10
|
from mako.exceptions import RichTraceback try : 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() 函數即可使用:
1
2
3
|
from djangomako.shortcuts import render_to_response def hello_view(request): return render_to_response( 'hello.txt' , { 'name' : 'yeolar' }) |
在Tornado中集成Mako
在Tornado中可以直接使用Mako,下面是一個使用示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
import tornado.web import mako.lookup import mako.template LOOK_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)) |