tornado: template 之 各种node/block
template中的_parse方法是模板文法的解析器,而这个文件中一坨一坨的各种node以及block,就是解析结果的承载者,也就是说在经过parse处理过后,我们输入的tornado的html模板就变成了各种block的集合。
这些block和node的祖宗就是这个“抽象”类, _Node,它定义了三个方法定义,其中generate方法是必须由子类提供实现的(所以我叫它“抽象”类)。
理论上来说,当一个类成为祖宗类时,必定意味着这个类包含了一些在子类中通用的行为,那么,从_Node暴露出来的方法来看,即所有的子类理论上都会有如下特征:
1. 可作为容器 (each_child, find_named_blocks)
2. generate
当然了,理想总是丰满的,现实也总有那么点儿不对劲,对于某些子孙,它们的特征看上去不是那么靠谱,比如_Text。
_Text这个类只用到了generate这个方法,用于将文字(Html, JS)经过trim后添加到输入流中,如果调用它的each_child or find_named_blocks,当然你能这么做,但是没有什么意义。
前面反复说到_Parse方法,它返回的结果是一个_ChunkList的实例,而_ChunkList继承与_Node。这是一个体现了_Node容器特点的类,重写了generate方法和each_child方法,而基本上就是依次调用容器内所有元素的相关方法而已。
_Nodes众多子子孙孙中比较奇葩的是_ExtendsBlock这个类,丫什么事情都没做(That is true),看上去像是另外一个“抽象类”,但是居然会被_Parse初始化,用于处理Extends这个token(tornado术语)。我就纳闷了,一旦这货被generate,难道不会抛一个异常出来木?
真正有意思的是另外几个方法,它们有共通的模式,用_ApplyBlock来举例
在_ApplyBlock中,有趣的是generate方法
def generate(self, writer): method_name = "apply%d" % writer.apply_counter writer.apply_counter += 1 writer.write_line("def %s():" % method_name, self.line) with writer.indent(): writer.write_line("_buffer = []", self.line) writer.write_line("_append = _buffer.append", self.line) self.body.generate(writer) writer.write_line("return _utf8('').join(_buffer)", self.line) writer.write_line("_append(%s(%s()))" % ( self.method, method_name), self.line)简单来说,这个函数做了两件事情:
- 定义了一个python文件全局函数叫做applyXXX():,其中的XXX是一个整形的,自增的值,返回值是一个utf8字符串。
- 执行这个applyXXX函数,将此函数的输出再作为self.method这个函数的输入。
所以,如果一个类似于这样的模板
{%apply linkify%} {{address}} {%end%}
会得到一个类似于如下的输出:
r = applyXXX() r = linkify(r) _append(r)
结合这篇文章 http://autumn-sea.appspot.com/page/agphdXR1bW4tc2VhcgwLEgRCbG9nGLKDfww (需科学上网),可以了解到,tornado的template机制,本质上讲,就是允许开发者已HTML + template marker的方式来编写视图模板,但是在背后,tornado会把这些视图模板通过template的处理,变成可编译的python代码。
还是那autumn-sea上面的代码作为例子,比较容易理解。
View Template
<html> <head> <title>{{ title }}</title> </head> <body> hello! {{ name }} </body> </html>
处理后
_buffer = [] _buffer.append('<html>\\n<head>\\n<title>') _tmp = title if isinstance(_tmp, str): _buffer.append(_tmp) elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode('utf-8')) else: _buffer.append(str(_tmp)) _buffer.append('</title>\\n</head>\\n<body>\\n') _buffer.append('hello! ') _tmp = name if isinstance(_tmp, str): _buffer.append(_tmp) elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode('utf-8')) else: _buffer.append(str(_tmp)) _buffer.append('\\n</body>\\n</html>\\n') return ''.join(_buffer)\n"
简单吧?听说这就是传说中的CGI,不过咱没弄过。。。