文章目录
  1. 1. 将template转成python代码
  2. 2. 编译python函数

这几天花了点时间读了下web.py的template代码,只是大概的过了下。主要想了解下Python中AST编译相关的模块使用方法。

先看个webpy template使用方法,来自其cookbook. 首先是template内容的html, 置于./templates/hello.html

1
2
$def with (name)
Hello $name!

在代码里初始化render, 传递文件夹名为参数,然后就可以调用和模板同名的方法了,生成的html以字符串返回。

1
2
render = web.template.render('templates')
print render.hello('world')

web.py template的语法格式同样参考其cookbook,变量赋值或语句都以$开始;$if/$for/$while都需要单独一行,内容行首遵循python的缩进格式。template内容是一行行处理的, 生成nodelist,转换成python代码,然后再调用python 编译服务。

看代码当然是从github开始了, template 相关的代码在文件 webpy / web / template.py . template的初始化是从class Render开始,由render._load_template,模板类是class BaseTemplateclass Template。模板的解析工作由class Parser进行, 解析后生成的node类有DefwithNode, TextNode, ExpressionNode, AssignmentNode, LineNode, BlockNode, ForNode, CodeNode, StatementNode, IfNode, ElseNode, ElifNode, DefNode, VarNode, SuiteNode。每个node类都定义有emit()用于生成对应的python代码。

将template转成python代码

Templateinit()调用self.compile_template将template转成一个python函数的代码,再调用builtin方法compile()和exec()编译,当需要即执行新生成的函数。

将template转成python由Template.generate_code完成。这里略过Parse的细节。下面代码演示生成的python代码。

1
2
3
4
5
import web
filename = 'templates/test.html'
code = web.template.Template.generate_code(open(filename, 'r').read(), filename)
print code

以下为template test.html的内容,添加了一个if/else块:

1
2
3
4
5
6
7
$def with (name)
Hello $name!
$if name == 'world':
world again
$else:
only hello

生成的python代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# coding: utf-8
def __template__ (name):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'Hello ', escape_(name, True), u'!\n'])
extend_([u'\n'])
if name == 'world':
extend_([u'world again\n'])
else:
extend_([u'only hello\n'])
return self

编译python函数

生成代表python function的字符串后,还需要compile()exec()才能生成可调用的对象。

在前边生成的代码,可以看到有创建外部类的对象,ForLoop(),TemplateResult()和escape_, 需要在comile时导入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import web
filename = 'templates/test.html'
code = web.template.Template.generate_code(open(filename, 'r').read(), filename)
#print code
# compile it
compiled_code = compile(code, filename, 'exec')
def escape(value, escape = True):
if value is None:
return ''
return value
env = dict({},
ForLoop= web.template.ForLoop,
TemplateResult = web.template.TemplateResult,
escape_ = escape)
exec(compiled_code, env)
t = env['__template__']
print t('world')

生成的函数对象可以由env[‘template‘]获得。最后打印出的结果如下:

1
2
3
4
Hello world!
world again

文章目录
  1. 1. 将template转成python代码
  2. 2. 编译python函数