web.py源码分析: 模板(1),web.py源码, 实验环境搭建 为了说明
web.py源码分析: 模板(1),web.py源码, 实验环境搭建 为了说明
web.py模板的实现原理
web.py的模板实现利用了Python的可执行对象的动态特性:根据模板内容和渲染函数的参数创建一个函数,该函数执行的时候会返回一个TemplateResult类实例,该实例的字符串化就是模板对应的HTML内容。
实验环境搭建
为了说明web.py的模板是如何实现的,我们需要在web.py的模板实现代码中加入一些打印语句来显示中间结果。Python的virtualenv工具很好的实现了这个需求。另外,我还使用了iPython,不过Python标准命令行也是可以的。环境搭建的步骤简述如下:
- 创建virtualenv环境:
virtualenv env
- 激活virtualenv环境:
cd env
以及source bin/activate
- 安装web.py:
pip install web.py
这个web.py库会被安装在virtualenv环境的目录下:
(env)➜ ~/programming/python/env/lib/python2.7/site-packages/web $ pwd /home/diabloneo/programming/python/env/lib/python2.7/site-packages/web
下面就可以修改这个web.py的代码来看看模板到底是如何实现的。
实验代码修改
我们要修改的代码位于web/template.py文件内,找到Template类的compile_template函数(在template.py文件的第900行),加入一行打印语句:
def compile_template(self, template_string, filename): code = Template.generate_code(template_string, filename, parser=self.create_parser()) def get_source_line(filename, lineno): try: lines = open(filename).read().splitlines() return lines[lineno] except: return None print code # 这行就是我们增加的调试语句,可以打印出前面提到的动态生成的函数。 try: # compile the code first to report the errors, if any, with the filename compiled_code = compile(code, filename, 'exec') except SyntaxError, e: ...
模板函数到底长什么样?
下面我们就可以来看看模板函数到底长什么样了。当然,首先得创建一个模板文件。在我们的实验环境中进行如下操作:
(env)➜ ~/programming/python/env $ ls bin include lib local (env)➜ ~/programming/python/env $ mkdir app (env)➜ ~/programming/python/env $ ls app bin include lib local (env)➜ ~/programming/python/env $ cd app (env)➜ ~/programming/python/env/app $ mkdir templates
现在,在templates目录下创建一个最简单的模板,文件名为hello.html,内容如下:
hello, world
下面来看看根据这个模板生成的函数到底长什么样子,在实验环境下启动ipython或者python,进入到app目录:
(env)➜ ~/programming/python/env/app $ ipython WARNING: Attempting to work in a virtualenv. If you encounter problems, please install IPython inside the virtualenv. Python 2.7.8 (default, Oct 20 2014, 15:05:19) Type "copyright", "credits" or "license" for more information. IPython 1.0.0 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]:
执行如下代码就可以看到模板函数的内容:
In [3]: hello = web.template.frender("templates/hello.html") # coding: utf-8 def __template__(): __lineoffset__ = -5 loop = ForLoop() self = TemplateResult(); extend_ = self.extend extend_([u'hello, worldn']) return self In [4]:
上面的函数__template__()就是我们增加到web.py库中的那行print code
所打印出来的。从这个函数的定义,我们可以看出:
- 函数内使用了一些预定义的对象:ForLoop,TemplateResult等。
- 函数的结果是返回一个TemplateResult实例。
那么,现在问题来了:
- 这个函数是如何生成的?
- 使用的这些预定义名称从哪里来的?
- TemplateResult实例的字符串表示如何变成HTML文本的?
本文先来说明TemplateResult实例是如何产生HTML文本这个问题。
TemplateResult
TemplateResult类的定义也在web/template.py文件中,继承的类和实现的方法如下:
class TemplateResult(object, DictMixin): __delattr__ : function __delitem__ : function __getattr__ : function __getitem__ : function __init__ : function __repr__ : function __setattr__ : function __setitem__ : function __str__ : function __unicode__ : function _prepare_body : function keys : function
其中DictMixin是一个实现了大部分字典操作的类,继承该类的子类(也就是这里的TemplateResult)需要实现:__getitem__(), __setitem__(), __delitem__()和keys()方法,以便对象可以模拟完整的字典操作。需要说明的是:DictMixin类已经过时了,现在应该使用collections.MutableMapping类(该类的实现利用了abc库–抽象类)。
先来看__init__()函数,
def __init__(self, *a, **kw): self.__dict__["_d"] = dict(*a, **kw) self._d.setdefault("__body__", u'') self.__dict__['_parts'] = [] self.__dict__["extend"] = self._parts.extend
从初始化函数可以看出,TemplateResult大部分属性都存储在_d这个字典中,该字典至少包含一个元素body。所以,外部代码对TemplateResult的对象属性进行增删改查操作时,实际上都是在操作内部的_d这个字典。初始化函数中定义的另外两个属性是:_parts,一个序列;extend,指向_parts序列的extend()方法,也就是说调用TemplateResult实例的extend方法实际上是调用实例属性_parts的extend方法。这个extend方法我们在前面的__template__()函数已经见到过了:
def __template__(): __lineoffset__ = -5 loop = ForLoop() self = TemplateResult(); extend_ = self.extend extend_([u'hello, world\n']) return self
这个函数定义了extend_ = self.extend
,extend_把模板的内容添加到了self._parts这个序列中。
下面来看一下TemplateResult如何生成HTML内容,这个就要看__str__()方法了:
def _prepare_body(self): """Prepare value of __body__ by joining parts. """ if self._parts: value = u"".join(self._parts) self._parts[:] = [] body = self._d.get('__body__') if body: self._d['__body__'] = body + value else: self._d['__body__'] = value def __str__(self): self._prepare_body() return self["__body__"].encode('utf-8')
主要操作是在_prepare_body()函数里,主要操作是把_parts中的字符串拼接起来,然后再拼接到body的内容后面。
看过TemplateResult的实现后,我们可以知道,由模板生成的__template__()函数最终会把一堆字符串添加到TemplateResult实例中,然后再通过实例生成HTML字符串。
总结
通过打印中间结果和分析代码,我们已经大概知道了web.py的模板是如何转化成HTML内容的。下一篇文章会阐述web.py模板的各种语法所对应的动态函数内容。
Next–>
相关内容
- 又是 web.py 页面执行计时,web.py页面执行计时,实现计时
- 对web.py设置favicon.ico的方法详解,web.pyfavicon.ico
- 浅析Python的web.py框架中url的设定方法,pythonweb.py
- 在windows下快速搭建web.py开发框架方法,web.py框架
- web.py 十分钟创建简易博客实现代码,web.py十分钟
- web.py在SAE中的Session问题解决方法(使用mysql存储),
- web.py在模板中输出美元符号的方法,web.py美元
- web.py中调用文件夹内模板的方法,web.py模板
- web.py获取上传文件名的正确方法,web.py获取上传
- Python开发WebService系列教程之REST,web.py,eurasia,Django
评论关闭