Mako 模板系统文档翻译(4) 运行时环境
原文:http://www.makotemplates.org/docs/runtime.html
译文:
Version: 0.1.5 Last Updated: 05/01/07 20:21:35
Mako 的运行时环境
本节会介绍一点模板中可访问的对象和内建函数的内容。
Context
Context 是模板被第一次执行前创建的一个核心对象,它负责和模板外部做所有的交互。它由两个主要的组件组成,1. 输出缓冲区,这是一个类似文件的对象,比如 Python 的 StringIO;2. 变量字典,其中的所有变量均可在模板中自由引用,该字典是由传递给 template.rener() 方法的参数,以及一些由 Mako 运行时环境提供的内建变量组成。
缓冲区
缓冲区存储在 Context 中,通过 context.write() 方法可以写缓冲区。通常你不需要关注此方法,因为模板中的文本和 ${} 形式的表达式,都会自动将输出提交到该方法。只有在下列几个应用场景下你才可能需要用到它:1. 处理各种过滤/缓冲(详见 Filtering and Buffering),2. 用编程的方式将内容发送到输出流,比如在一个 <% %> 块中。
<%
context.write("some programmatic text")
%>
实际的缓冲区可能不是原来发送给 Context 对象的那个,因为在各种过滤/缓存的场景下,可能会 "push" 一个新的缓冲区到 context 内部的缓冲区栈上。正因为此,只要我们记住始终调用 context.write() 方法,就可以确保内容被发送到顶层的缓冲区。
上下文变量
当模板被编译为 python 模块时,其页面内容被包含在 render_body 这个函数中。其他顶层的 defs, 会在其各自独立的函数中定义,函数名被附加了一个前缀 "render_"(比如 render_mydef)。在这些函数中,所有在本地没有定义的变量(比如通过赋值或模块导入的方式定义的),都会从 Context 对象的变量字典中提取。
-
如果引用当前上下文中不存在的变量会怎么样? - 你取得的值将是一个特殊的值 UNDEFINED. 它是 mako.runtime.Undefined 类的一个全局变量。UNDEFINED 对象会在你尝试调用 str() 时抛出错误。这会在尝试在表达式中使用它的时候发生。
-
为什么不直接返回 None 呢? UNDEFINED 更明确,可以和人为传递到 Context 中的 None 加以区分,以及和没有提供值的变量区分开来。
-
为什么对它调用 str() 时会引发异常,而不是返回空字符串呢? - Mako 一直在努力遵循 python 的哲学 “明确胜于隐晦”(explicit is better than implicit). 具体来说,模板作者应该处理值丢失的情况,而不是默默的任由其出错。因为 UNDEFINED 和 python 的 True 或 False 一样,是个单件(Singleton) 对象,你可以用 is 运算符来检查它:
% if someval is UNDEFINED: someval is: no value % else: someval is: ${someval} % endif
另一个值得注意的方面是,Context 的变量字典是不可变的。当然,因为是纯 python 的方式,你可以修改 context 的变量字典中的变量,但这恐怕不会如你想像。原因是,Mako 会在很多情况下会创建 Context 对象的副本,并将这些副本传递给模板中的各种元素,以及执行过程中可能用到的子模板。所以,修改本地 Context 中的值,不一定会在模板的其他部分生效。Mako 创建 Context 副本的一个例子是,在模板体中进行对顶层 def 的调用(context 被用于传递局部变量到 def 的范围中;因为在模板体内,他们以内联函数的形式出现,Mako 会尝试让他们用这种方式工作)。另一个例子是在继承链中(在链中的每个模板都有其不同的 parent 和 next,而这两个变量就保存在各自唯一的 Context 对象中)。
- 那么,我们如何设定相对于一个模板请求过程的全局变量呢? - 只要在模板初次运行时给 Context 提供一个字典即可,然后所有的地方就都可以向该字典中 get/set 变量了。比如叫做 attributes:
运行模板:
output = template.render(attributes={})
在模板中,直接引用该字典:
<%
attributes['foo'] = 'bar'
%>
'foo' attribute is: ${attributes['foo']}
- 为什么"attributes" 不是 Context 的内建特性呢? - 这也是 Mako 替你的应用程序尽量少做决定的一个体现。也许你不想在模板里用这种技术读写和共享数据,又或者你想用不同的变量名或数据结构来传递。再一次的,Mako 宁愿用户明确一点。
上下文方法和访问器(Context Methods and Accessors)
Context 的主要成员包括:
-
context[key]
/context.get(key, default=None)
- 类似字典方式的访问器。通常你在模板中使用的变量,如果在局部没有定义,则会去上下文中获取。当你要使用一个在别处已经定义的(比如传递给 %def 调用的局部参数)变量时,可以用字典访问语法或 get 方法。如果没有提供 key, 则和字典类似,会引发 KeyError. -
keys()
- 上下文中已定义的所有名称。 -
kwargs
- 这会返回一个上下文变量字典的副本。当你想把当前上下文中的变量传递到函数的关键字参数时,这样做很有用。例如:${next.body(**context.kwargs)}
-
write(text)
- 向当前输出流中写一些文本。 -
lookup
- 返回当前执行中用于所有文件查找请求的 TemplateLookup 对象(尽管每个独立的 Template 实例可以有其不同的 TemplateLookup 实例,但只有最初调用到的那个 Template 的 TemplateLookup 会被使用)。
All the built-in names
现在来看看 Mako 定义了的所有名称。下面的大多数名称是 Namespace 的实例(下一节中有详细介绍:Namespaces),并且,下面除了 context 和 UNDEFINED 之外的大多数变量名称都定义在 Context 中。
local
- 当前模板的名称空间, described in Built-in Namespacesself
- 继承链顶层模板的名称空间 (如果有的话,没有则和 local 一样), mostly described in Inheritanceparent
- 继承链中父模板的名称空间(或者未定义); see Inheritancenext
- 继承链中下一个模板的名称空间(或未定义); see Inheritancecaller
- 当使用 <%call> 标签定义一个“带内容的调用”时创建的一个“迷你”名称空间。 described in Calling a def with embedded content and/or other defscapture
- 一个函数,用于调用指定的 def 并捕获其输出内容,返回为字符串。 Usage is described in BufferingUNDEFINED
- 一个全局的单件对象,用于在 Context 中找不到的变量的返回值。是mako.runtime.Undefined
类的实例,调用其 __str__() 方法会引发异常。pageargs
- 在没有在 <%page> 标签中定义任何关键字参数的模板中有效,是一个字典。所有传递给模板 body() 函数(当通过名称空间使用时)的关键字参数,都会被收集到这个字典中,其他则被定义为页面参数。(如果已经预先声明)。[原文:All keyword arguments sent to thebody()
function of a template (when used via namespaces) go here by default unless otherwise defined as a page argument.] If this makes no sense, it shouldn't; read the section The "body()" method.