Tornado模板系統是將模板編譯成Python代碼。html
最基本的使用方式:python
t = template.Template("<html>{{ myvalue }}</html>") print t.generate(myvalue="XXX")
Loader這個類加載根目錄的模板,而後緩存編譯好的模板。web
tornado模板系統不像其餘模板系統,咱們不會在表達式上設置任何限制。If 和for語句塊都會被精確地轉成Python代碼,因此你能夠像下面一個使用複雜的表達式。json
{% for student in [p for p in people if p.student and p.age > 23] %} <li>{{ escape(student.name) }}</li> {% end %}
### Python code def add(x, y): return x + y template.execute(add=add) ### The template {{ add(1, 2) }}
默認狀況下,咱們提供escape,url_escape,json_encode以及squeeze函數給全部的模板。緩存
通常狀況下,應用不會建立Template或者Loader實例,而是使用tornado.web.RquestHandler的 render以及render_string 方法,這兩個方法根據Application setting的template_path選項的值自動加載模板。多線程
以 _tt_ 開始的變量名稱是被模板系統保留的,應用程序不能使用。併發
模板表達式使用雙方括號{{…}}包圍。裏面內容能夠是任何python的表達式,裏面的內容根據當前的autoescape設置而決定是否進行轉義。其餘模板指令使用{%...%}.若是你須要將{{ 或者{%原樣輸出,你可使用{{!和 {%!進行轉義。app
註釋部分使用{#...#},從而註釋部分會被忽略,而不會輸出。函數
{% apply *function* %} ….{% end %}tornado
在apply 和end之間對模板的全部輸出應用函數。好比:
{% apply linkify %}{{name}} said: {{message}}{% end %}
{% block *name* %}...{% end %} 表示一個命名的、能夠被替換的塊看,這個語法是針對模板繼承的使用 {%
extends
%}。在父模板中的塊會被子模板同名的塊中內容替換。好比:
<!-- base.html --> <title>{% block title %}Default title{% end %}</title> <!-- mypage.html --> {% extends "base.html" %} {% block title %}My page title{% end %}
{% comment ... %} 表示註釋,是不會被輸出的。注意這裏沒有{% end %}標籤。
{% extends *filename* %} 繼承模板。
{% for *var* in *expr* %}...{% end %} 跟Python for循環語句相同。{%
break
%}和{%
continue
%} 能夠在循環內使用。
{% from *x* import *y* %} 跟Python import語句同樣
{% if *condition* %}...{% elif *condition* %}...{% else %}...{% end %} 條件語句
{% import *module* %} 跟Python import語句同樣
{% include *filename* %}引用其餘模板文件。這個引用的文件能夠看到全部的本地變量,由於他們都被直接複製。相反地,{%
module
Template(filename,
**kwargs)
%} 被用來包含其餘在隔離命名空間的模板。
{% set *x* = *y* %}設置值
{% try %}...{% except %}...{% else %}...{% finally %}...{% end %}跟Python的異常處理同樣。
{% while *condition* %}... {% end %}跟Python的while語句同樣。
源代碼位於site-packages > tornado > template.py,這個類表示已編譯好的模板。
構造函數: def __init__(self, template_string, name="<string>", loader=None, compress_whitespace=None, autoescape=_UNSET):
參數:
template_string :模板字符串
name :模板名稱
loader: 模板加載器
compress_whitespace :是否壓縮空白字符
autoescape :是否自動轉義
實現代碼:
self.name = name if compress_whitespace is None: compress_whitespace = name.endswith(".html") or \ name.endswith(".js") if autoescape is not _UNSET: self.autoescape = autoescape elif loader: self.autoescape = loader.autoescape else: self.autoescape = _DEFAULT_AUTOESCAPE self.namespace = loader.namespace if loader else {} reader = _TemplateReader(name, escape.native_str(template_string)) self.file = _File(self, _parse(reader, self)) self.code = self._generate_python(loader, compress_whitespace) self.loader = loader try: # Under python2.5, the fake filename used here must match # the module name used in __name__ below. # The dont_inherit flag prevents template.py's future imports # from being applied to the generated code. self.compiled = compile( escape.to_unicode(self.code), "%s.generated.py" % self.name.replace('.', '_'), "exec", dont_inherit=True) except Exception: formatted_code = _format_code(self.code).rstrip() app_log.error("%s code:\n%s", self.name, formatted_code) raise
處理過程:
(1) 對
compress_whitespace的值進行設置。若是是None,若是模板是html或者js文件,則compress_whitespace 爲True.
(2) 對 autoescape 的設置。autoescape是一個函數,默認爲「xhtml_escape」函數
(3) 對namespace 命名空間的設置。若是設置了loader,則爲loader的namespace.
(4)
設置內部類_TemplateReader類型的reader.
(5)
設置file、code屬性。
(6)
最後調用compile方法,進行模板編譯。
def generate(self, **kwargs): """Generate this template with the given arguments.""" namespace = { "escape": escape.xhtml_escape, "xhtml_escape": escape.xhtml_escape, "url_escape": escape.url_escape, "json_encode": escape.json_encode, "squeeze": escape.squeeze, "linkify": escape.linkify, "datetime": datetime, "_tt_utf8": escape.utf8, # for internal use "_tt_string_types": (unicode_type, bytes), # __name__ and __loader__ allow the traceback mechanism to find # the generated source code. "__name__": self.name.replace('.', '_'), "__loader__": ObjectDict(get_source=lambda name: self.code), } namespace.update(self.namespace) namespace.update(kwargs) exec_in(self.compiled, namespace) execute = namespace["_tt_execute"] # Clear the traceback module's cache of source data now that # we've generated a new template (mainly for this module's # unittests, where different tests reuse the same name). linecache.clearcache() return execute()
做用:根據給定的參數,生成模板,最後是生成模板的結果,是一個字符串。
參數:
kwargs:是一個namespace的字典,都是一個方法的映射。
模板加載器的基類。
當你使用像 「{% extends %}」、{% include% }這些模板語法時,你必須使用模板加載器。當模板第一次加載完畢後,加載器會緩存這些模板。
def __init__(self, autoescape=_DEFAULT_AUTOESCAPE, namespace=None): """``autoescape`` must be either None or a string naming a function in the template namespace, such as "xhtml_escape". """ self.autoescape = autoescape self.namespace = namespace or {} self.templates = {} # self.lock protects self.templates. It's a reentrant lock # because templates may load other templates via `include` or # `extends`. Note that thanks to the GIL this code would be safe # even without the lock, but could lead to wasted work as multiple # threads tried to compile the same template simultaneously. self.lock = threading.RLock()
參數:
autoescape: 是否自動轉義。要麼是None值,要麼是在模板namespace中的函數名,好比是」xhtml_escape」
namespace:模板中使用的方法的映射字典。
處理過程:
主要是對相關屬性的初始化。注意,最後一句self.lock= threading.RLock() 是爲了考慮多線程併發的存在,加上線程鎖。
重設加載器的模板,清除緩存
將模板的相對路徑轉換成絕對路徑,由子類繼承
加載模板。調用了resolve_path以及_create_template方法,這兩個方法都有子類實現。
def load(self, name, parent_path=None): """Loads a template.""" name = self.resolve_path(name, parent_path=parent_path) with self.lock: if name not in self.templates: self.templates[name] = self._create_template(name) return self.templates[name]
模板加載器,繼承BaseLoader,從單一根路徑加載。
def __init__(self, root_directory, **kwargs): super(Loader, self).__init__(**kwargs) self.root = os.path.abspath(root_directory)
參數:
root_directory:根路徑
kwargs:參數字典
處理過程:
調用父類初始化過程,同時設置root屬性,爲絕對路徑
實現BaseLoader父類中的方法。
def resolve_path(self, name, parent_path=None): if parent_path and not parent_path.startswith("<") and \ not parent_path.startswith("/") and \ not name.startswith("/"): current_path = os.path.join(self.root, parent_path) file_dir = os.path.dirname(os.path.abspath(current_path)) relative_path = os.path.abspath(os.path.join(file_dir, name)) if relative_path.startswith(self.root): name = relative_path[len(self.root) + 1:] return name
就是講各類路徑合併,而後獲得模板的絕對路徑,最後去除根路徑,返回剩餘的路徑。
def _create_template(self, name): path = os.path.join(self.root, name) with open(path, "rb") as f: template = Template(f.read(), name=name, loader=self) return template
根據模板的絕對路徑,打開模板文件,而後實例化Template對象並返回。實例化的Template對象會緩存在加載器中。