mako模版库实践

article/2025/9/11 5:28:58

介绍

这几天在编写一个数据驱动测试框架,其中一个核心模块是根据数据输入,自动转换为测试用例代码。测试数据格式固定,对应的用例代码结构也相对稳定。基于此场景,联想到web页面根据数据从template渲染页面,决定使用类似的方式,采用模版库动态生成测试用例代码。
Python库有两个常用的模版库:Jinja2和Mako。两个库功能都很齐全强大,Jinja2广泛用于各种语言环境如Java/Go,Mako主要用于Python语言环境。
Mako模板从一个包含各种类型的内容的文本流解析得到,包括XML、HTML、email文本等。模板还可以包含Mako指令,用来表示变量和表达式替换、控制结构、服务器端注释、整块Python代码,还有用来提供额外功能的各种标签。所有这些结构都被编译为实际的Python代码。
Mako能直接嵌套Python代码,更贴近python代码风格,项目使用的也是python语言环境,使用起来更加方便,网上也有说mako库解析速度更快,我们最终选择了mako库。

使用方式

下面总结一下mako使用方式。
使用Template
通过from mako.template import Template引入mako模板。
1)基本用法

from mako.template import Template
t = Template("Hello,${name}")
print(t.render(name = 'world'))

输出:

Hello,world

2)导入模版文件
Template函数还有一些常用的参数:

  • filename:指定从文件中加载模板。最好用相对路径,否则缓存文件会包含在很长的路径下
  • module_directory:缓存目录。从文件中加载模板,可以指定module_directory生成.py文件缓存,提高加载速度。

例如:
新建一个模版文件demo.tmpl,保存以下内容:

Hello,${name}

使用模版代码:

from mako.template import Template
t = Template(filename="demo.tmpl", module_directory="./")
print(t.render(name='world'))

代码执行以后,当前目录下会生成一个以模版文件名,再加上后缀“.py”的缓存文件。
在这里插入图片描述
rend()方法会创建一个Context对象,该对象会存储所有render()方法传递给模版的变量,并且会保存为buffer用于捕获output输出。
我们也可以自定义Context对象,然后用 render_context()方法渲染。

from mako.template import Template
from mako.runtime import Context
from io import StringIOmytemplate = Template("hello, ${name}")
buf = StringIO()
ctx = Context(buf, name="python")
mytemplate.render_context(ctx)
print(buf.getvalue())

使用TemplateLookup
实际项目中,通常会有多个模版文件,组合渲染一个页面。这种情况下,我们可以使用TemplateLookup,直接从指定的目录导入多个模版文件。

from mako.lookup import TemplateLookuplookup = TemplateLookup(directories=['./templates'], module_directory='./', collection_size=500, filesystem_checks=True)
"""
# 使用templateLookup,可以方便的设置模板目录,当模板中include别的模板时可以只写模板名
# collection_size设置加载到内存中的模板上限
# filesystem_checks为True会在每次加载模板缓存判断模板文件是否有改动,会重新编译。
"""
def serve_template(templatename, **kwargs):mytemplate = lookup.get_template(templatename) #get_template()方法根据文件名,获取指定的templateprint(mytemplate.render(**kwargs))serve_template("index.html", name="python")

异常处理
异常主要出现在查找并解析模版文件,以及渲染模版两个场合。渲染阶段的异常主要是python自带的异常。mako封装了异常类处理模版解析过程中的异常堆栈,将堆栈数据格式输出化为text或者html格式。
text_error_template() 输出text格式异常堆栈;html_error_template() 输出html格式异常堆栈。这两个方法本质上都是调用sys.exc_info()获取程序最近抛出的异常。

  • text模版异常
from mako import exceptions
try:template = lookup.get_template(uri)print(template.render())
except:print(exceptions.text_error_template().render())
  • html格式模版异常
from mako import exceptionstry:template = lookup.get_template(uri)print(template.render())
except:print(exceptions.html_error_template().render())
  • 自定义异常:
    exceptions类底层使用的RichTraceback对象。该对象也可以用来自定义异常。
from mako.exceptions import RichTracebacktry:template = lookup.get_template(uri)print(template.render())
except:traceback = RichTraceback()for (filename, lineno, function, line) in traceback.traceback:print("File %s, line %s, in %s" % (filename, lineno, function))print(line, "\n")print("%s: %s" % (str(traceback.error.__class__.__name__), traceback.error

语法

表达式替换

  • 最简单的表达式是变量替换。语法为 ${var}
    Hello, this is my name: ${var}
    上面的例子会把 var 的字符串表示输出到模板的输出流。 var通常来自于传递给模板渲染函数的 Context 。如果没有传入x 给模板,并且也没有本地赋值,那么就等于一个特殊的值 UNDEFINED 。
    例如,模版文件demo.tmpl包含以下内容:
Hello, this is my name: ${name}
print(${version})

但是render()方法没有传递version参数时,会抛出异常“NameError: Undefined”

from mako.template import Template
t = Template(filename="./templates/demo.tmpl", module_directory="./")
print(t.render(name='world'))

运行结果:

Traceback (most recent call last):File "/Users/bruce.xu/mako/basic_usage.py", line 19, in <module>print(t.render(name='world'))File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mako/template.py", line 476, in renderreturn runtime._render(self, self.callable_, args, data)File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mako/runtime.py", line 883, in _render**_kwargs_for_callable(callable_, data)File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mako/runtime.py", line 920, in _render_context_exec_template(inherit, lclcontext, args=args, kwargs=kwargs)File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mako/runtime.py", line 947, in _exec_templatecallable_(context, *args, **kwargs)File "/Users/bruce.xu/mako/templates/demo.tmpl.py", line 26, in render_body__M_writer(str(version))File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mako/runtime.py", line 230, in __str__raise NameError("Undefined")
NameError: Undefined
  • ${} 标签中的内容由Python直接计算,因此也可以使用完整的表达式。
Hello, sum is: ${a + b}

输出结果:

Hello, sum is: 3

表达式转义/过滤器
Mako有一组内建的转义机制,包括HTML、URI和XML转义,还有空白截断函数。

  • 可以用 | 操作添加到表达式替换
    ${“this is some text” | u}
    上面将URL转义应用到表达式上,生成 this+is+some+text 。 u 表示URL转义, h 表示HTML转义, x 表示XML转义, trim 为空白截断函数。
    更多有关内建的过滤函数,包括如何编写自己的过滤函数,见 Filtering and Buffering 。

  • 可以用逗号分割,使用多个转义
    ${" some value " | h,trim}
    转义后输出:

&lt;tag&gt;some value&lt;/tag&gt;
  • 自定义转义
    实际上就是自定义一个python函数,实现filter。
    例如,自定义python代码块
<%!def myescape(text):return "<TAG>" + text + "</TAG>"
%>
Here's some tagged text: ${"text" | myescape}

输出:

Here's some tagged text: <TAG>text</TAG>

也可以从python代码中直接导入filter:

<%!import myfilters
%>
Here's some tagged text: ${"text" | myfilters.tagfilter}

页面可以通过expression_filter参数设置默认过滤器

<%page expression_filter="h"/>
Escaped text:  ${"<html>some html</html>"}

Template和TemplateLookup可以通过default_filters参数指定默认过滤器。

t = TemplateLookup(directories=['/tmp'], default_filters=['unicode'])

也可以通过imports参数导入自定义的过滤器

t = TemplateLookup(directories=['/tmp'],default_filters=['unicode', 'myfilter'],imports=['from mypackage import myfilter'])

过滤器也可以用在defs和blocks上

<%def name="foo()" filter="h, trim"><b>this is bold</b>
</%def>

缓存buffering
缓冲区存储在 Context 中,通过 context.write() 方法可以写缓冲区。通常你不需要关注此方法,因为模板中的文本和 ${} 形式的表达式,都会自动将输出提交到该方法。只有在下列几个应用场景下你才可能需要用到它:1. 处理各种过滤/缓冲(详见 Filtering and Buffering),2. 用编程的方式将内容发送到输出流,比如在一个 <% %> 块中。
<%
context.write(“some programmatic text”)
%>
实际的缓冲区可能不是原来发送给 Context 对象的那个,因为在各种过滤/缓存的场景下,可能会 “push” 一个新的缓冲区到 context 内部的缓冲区栈上。正因为此,只要我们记住始终调用 context.write() 方法,就可以确保内容被发送到顶层的缓冲区。

Mako模版设计目的之一更快的执行效率。为了实现这个目标,template中的文本内容默认会直接通过管道pipe保存到Context对象的buffer缓冲区。这种设计,也带来一些副作用。其中之一,自定义defs函数之后,在模版中使用${somedef()}函数,该函数将会按照它在整个模版文件中首次定义的顺序显示。
例如:

<%def name="func()">func_data
</%def>
${"the result is:" + func()}

输出:

 func_data
the result is:

为了避免这种问题,我们可以在def定义时设置buffered=“True”

<%def name="func()" buffered="True">func_data
</%def>${"the result is:" + func()}

输出:

the result is:func_data

以上定义等同于以下代码:

def func():context.push_buffer()try:context.write("func_data")finally:buf = context.pop_buffer()return buf.getvalue()

针对这种场景,我们也可以使用capture()功能。和buffer不同的是,capture在函数被调用时才执行。

<%def name="func()">func_data
</%def>${"the result is:" + capture(func)}

控制结构
控制结构用来控制程序流——条件(即 if/else )、循环(如 while 和 for )、还有 try/except 等。

  • Mako中,控制结构写成 % 标记后面跟正常的Python控制表达式,用 % 加标签 end 结束, 为表达式的关键词。
    需要注意的是:控制结构中,可以采用python方式直接通过变量名获取相应的值,不能采用普通文本${var}的方式。
    例如,模版文件包含以下内容:
% if x==5:this is some output
% endif

执行render():

from mako.template import Template
t = Template(filename="./templates/demo.tmpl", module_directory="./")
print(t.render(x=5))

输出:

this is some output
  • % 可以放在前面没有文本的行的任何地方,并且缩进是不敏感的。Python所有的“冒号”表达式都可以使用,包括if/elif/else 、 while 、 for ,甚至是 def ,不过对 def Mako有更好的内建标签。
% for a in ['one', 'two', 'three', 'four', 'five']:% if a[0] == 't':its two or three% elif a[0] == 'f':four/five% else:one% endif
% endfor
  • % 符号也可以被转义,如果你想在行首使用百分比符号,可以用 %% 来转义:
%% some text%% some more text

上下文变量

Context 是模板被第一次执行前创建的一个核心对象,它负责和模板外部做所有的交互。它由两个主要的组件组成,

  1. 输出缓冲区,这是一个类似文件的对象,比如 Python 的 StringIO;
  2. 变量字典,其中的所有变量均可在模板中自由引用,该字典是由传递给 template.rener() 方法的参数,以及一些由 Mako 运行时环境提供的内建变量组成。
    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 会被使用)。
  • 通过context[key] / context.get(key, default=None)可以获取变量val对应的值
    例如,定义了模版文件
% for key in context.keys():
The key is <tt>${key}</tt>, the value is ${str(context.get(key))}. <br />
% endfor

使用render()传入参数

from mako.template import Template
t = Template(filename="./templates/demo.tmpl", module_directory="./")
print(t.render(a=5, b=4))

输出:

The key is <tt>a</tt>, the value is 5. <br />
The key is <tt>b</tt>, the value is 4. <br />
The key is <tt>capture</tt>, the value is functools.partial(<function capture at 0x105e30ea0>, <mako.runtime.Context object at 0x105e352b0>). <br />
The key is <tt>caller</tt>, the value is [None]. <br />
The key is <tt>self</tt>, the value is <mako.runtime.TemplateNamespace object at 0x105e352e8>. <br />
The key is <tt>local</tt>, the value is <mako.runtime.TemplateNamespace object at 0x105e352e8>. <br />
  • render传递参数attributes={}。可以在模版文件中通过attributes字典赋值的方式,动态增加全局变量。
    当模板被编译为 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:

<%attributes['foo'] = 'bar'
%>
'foo' attribute is: ${attributes['foo']}

render()代码:

from mako.template import Template
t = Template(filename="./templates/demo.tmpl", module_directory="./")
print(t.render(attributes={}))

输出:

'foo' attribute is: bar

循环上下文

  • loop.index 提供循环上下文的索引信息:
<ul>
% for a in ("one", "two", "three"):<li>Item ${loop.index}: ${a}</li>
% endfor
</ul>

输出:

<ul><li>Item 0: one</li><li>Item 1: two</li><li>Item 2: three</li>
</ul>
  • loop.cycle同时遍历多个迭代对象
<ul>
% for item in ('spam', 'ham', 'eggs'):<li class="${loop.cycle('even', 'odd')}">${item}</li>
% endfor
</ul>

输出:

<ul><li class="even">spam</li><li class="odd">ham</li><li class="even">eggs</li>
</ul>
  • loop.parent获取嵌套循环父循环上下文
    例如:
<table>
% for consonant in 'pbj':<tr>% for vowel in 'iou':<td class="${'black' if (loop.parent.even == loop.even) else 'red'}">${consonant + vowel}t</td>% endfor</tr>
% endfor
</table>

输出:

<table><tr><td class="black">pit</td><td class="red">pot</td><td class="black">put</td></tr><tr><td class="red">bit</td><td class="black">bot</td><td class="red">but</td></tr><tr><td class="black">jit</td><td class="red">jot</td><td class="black">jut</td></tr>
</table>

其他内建属性

  • local - 当前模板的名称空间, described in Built-in Namespaces
  • self - 继承链顶层模板的名称空间 (如果有的话,没有则和 local 一样), mostly described in Inheritance
  • parent - 继承链中父模板的名称空间(或者未定义); see Inheritance
  • next - 继承链中下一个模板的名称空间(或未定义); see Inheritance
  • caller - 当使用 <%call> 标签定义一个“带内容的调用”时创建的一个“迷你”名称空间。 described in Calling a def with embedded content and/or other defs
  • capture - 一个函数,用于调用指定的 def 并捕获其输出内容,返回为字符串。 Usage is described in Buffering
  • UNDEFINED - 一个全局的单件对象,用于在 Context 中找不到的变量的返回值。是 mako.runtime.Undefined 类的实例,调用其 str() 方法会引发异常。
  • pageargs - 在没有在 <%page> 标签中定义任何关键字参数的模板中有效,是一个字典。所有传递给模板 body() 函数(当通过名称空间使用时)的关键字参数,都会被收集到这个字典中,其他则被定义为页面参数。(如果已经预先声明)。[原文:All keyword arguments sent to the body() 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.

注释
有两种形式的注释。

  • 单行注释在行首使用 ## :
    ##this is a comment.
    …text …
  • 多行注释通过 <%doc> …text… </%doc> :
    <%doc>
    these are comments
    more comments
    </%doc>
  • 注意:若模板中存在中文应指点个编码,使用## coding=utf-8

python代码块

  • 可以用 <% %> 标签引入任意Python代码块
    在 <% %> 里面,可以写普通的Python代码块。代码的整体缩进不受限制,Mako编译器会根据生成的Python代码进行调节。
this is a template
<%x = db.get_resource('foo')y = [z.element for z in x if x.frobnizzle==5]
%>
% for elem in y:element: ${elem}
% endfor
  • 用 <%! %> 表示模块级的块。
    标签中的代码在模板的模块级别执行,而不是在模板的渲染函数中。因此,这些代码不能访问模板的上下文,并且只能在模板加载到内存时执行(一般每个应用一次,取决于运行环境)。使用 <%! %> 标签来声明模板的导入和纯Python函数:
<%!import mylibimport redef filter(text):return re.sub(r'^@', '', text)
%>

标签
Mako提供的其他东西以标签的形式出现。所有标签使用相同的语法,和XML标签类似,但是在标签名之前加上了 %字符。标签结束方式和XML类似:

<%include file="foo.txt"/>
<%def name="foo" buffered="True">this is a def
</%def>

每个标签都有一组属性。有些属性是必需的。很多属性支持运算,这意味着你可以在属性文本中嵌入一个表达式(使用 ${} ):

<%include file="/foo/bar/${myfile}.txt"/>

属性是否支持运行时运算取决于标签的类型和它编译到模板的方式。想知道能否添加表达式的最好方法就是试一下!词法分析器会告诉你是否有效。
下面是全部的标签的一个快速总结:

  • <%page>
    该标签定义了模板的一些通用特性,包括缓存参数和模板调用的参数的可选列表。
<%page args="x, y, z='default'"/>

也可以定义缓存特性:

<%page cached="True" cache_type="memory"/>

目前每个模板只有一个 <%page> 标签生效,其他的会被忽略。以后的版本中这会被改进,但是现在请确保你的模板中只用了一个 <%page> 标签,否则可能得不到你想要的结果。 <%page> 的用途参考 The body() Method 和 Caching

  • <%include>
    这是和其他模板语言类似的一个标签, %include 接受一个文件参数,调用那个文件的渲染结果:
<%include file="header.html"/>hello world
<%include file="footer.html"/>

它还接受 <%page> 标签的参数,应用到导入的模板上:
<%include file=“toolbar.html” args=“current_section=‘members’, username=‘ed’”/>

  • <%def>
    %def 标签定义一个Python函数,函数可以在模板的其他位置调用。
<%def name="myfunc(x)">this is myfunc, x is ${x}
</%def>
${myfunc(7)}

%def 标签比Python的 def 强大得多,因为Mako提供了很多额外的功能,比如能够将函数作为模板的“方法”,自动传递当前的 Context ,使用缓冲/过滤/缓存标志,作为参数传递给其他的函数调用。

  • <%block>
    <%block> 标签和 %def 类似,但它会在根作用域立即执行,而且可以是匿名的:
<%block filter="h">some <html> stuff.
</%block>

借鉴了Jinja2的块,有名字的块实现了一种很方便的继承方法:

<%block name="header">

<%block name="title"/>

  • <%namespace>
    Mako中的 %namespace 和Python的 import 语句等价。通过它可以访问其他模板的所有的渲染函数和metadata,Python模块,还有本地定义的函数“包”。
<%namespace file="functions.html" import="*"/>

%namespace 生成的底层对象是一个 mako.runtime.Namespace 实例。它是模板中的一个核心结构,用来引用模板的特定信息,比如当前的URI、继承结构和其他一些东西。名字空间见 Namespaces 。

  • <%inherit>
    通过继承可以实现模板的继承链。它和其他模板语言的概念类似。
<%inherit file="base.html"/>

当使用 %inherit 标签时,控制首先被传递到继承模板的顶层模板,由它来决定如何处理调用部分。Mako在这部分实现的非常灵活,包括动态继承、内容封装、多态方法调用

  • <%nsname:defname>
    可以通过 <%:> 来在一个名字空间中自定义一个标签。它的单标签和双标签形式分别对应行内表达式和 <%call> 标签。
<%mynamespace:somedef param="some value">this is the body
</%mynamespace:somedef>
  • <%text>
    该标签使Mako词法分析器跳过该部分的处理,返回整个内容。主要用于写Mako的文档:
<%text filter="h">heres some fake mako ${syntax}<%def name="x()">${x}</%def>
</%text>
  • 从模板中提前返回
    有时你想在模板或 <%def> 方法的中间停止处理,只用当前得到的结果。可以在Python代码块中使用 return 语句来实现。
% if not len(records):No records found.<% return %>
% endif

或:

<%if not len(records):return
%>

实践

了解了mako的基本语法以后,实践代码如下:

  • 定义模版文件code_template.tmpl
import pytest
from base import Basic
class ${cls_name}(Basic):device_type = "${context.get("device_type")}"device_info = Basic.get_device_info(device_type)device_id = device_info["device_id"]battery = device_info["battery_id"]
${gen_case_code()}
<%def name="gen_case_code()">
% for case in context.get("case_list"):<%case_keys = list(case.keys())case_name = case["case_name"]modes = case["tags"]case_info_list = case["case_info"]%>% for mode in modes:@pytest.mark.${mode}% endfordef test_${loop.index}_${case["case_name"]}(self, battery):% if "description" in case_keys:"""${case["description"]}"""% endif% for case_data in case_info_list:<%data_keys = list(case_data.keys())%>% if "sim_data" in data_keys:<% sim_data = case_data["sim_data"]%>simulator_data = ${sim_data}simulator_data["battery_id"] = batteryprint(simulator_data)pub_resp = self.dev_publish_msg(self.device_type, self.device_id, **simulator_data)if pub_resp.status_code == 200:print(pub_resp.json())% endifprint(battery)% if "api_name" in data_keys:api_info = self.get_api_with_name("${case_data["api_name"]}")print(api_info)resp = self.send_request(battery, **api_info)print(resp)assert resp.status_code == 200% if "expect" in data_keys:<%expect_data = case_data["expect"]%>if resp["result_code"] == "success":% for ex_k, ex_v in case_data["expect"].items():assert resp["data"]["${ex_k}"] == ${ex_v}% endfor% endif% endif% endfor
% endfor
</%def>
  • render()渲染代码:
def code_generator(device_type, case_list, cls_name, case_file):t = Template(filename='code_template.tmpl')try:resp = t.render(device_type=device_type, case_list=case_list, cls_name=cls_name)with open(case_file, 'w') as tmp_f:tmp_f.writelines(resp)print(t.render(device_type=device_type, case_list=case_list, cls_name=cls_name))except Exception:print(exceptions.html_error_template().render())

参考文献:
https://docs.makotemplates.org/en/latest/usage.html
https://www.cnblogs.com/zhaojianwei/p/4666651.html


http://chatgpt.dhexx.cn/article/hN4AQrYP.shtml

相关文章

Django——mako的配置与使用方法

一、前言 mako最大的特点就是允许在HTML中随意书写Python代码&#xff0c;挺起就很心动啊&#xff01;&#xff01; 二、准备阶段 新建一个项目mako_project&#xff0c;再新建一个app叫app&#xff0c;然后安装mako&#xff08;要联网安装&#xff09; pip install mako然…

Xen新特性

4.0 为了增强主机层面的性能和可扩展性&#xff0c;新的Xen 4.0虚拟机管理程序现在横跨主机服务器上的128(之前64 )个物理处理器&#xff0c;最多可以处理1TB的物理主系统内存。在访客环境中&#xff0c;基于Xen 4.0运行的虚拟机现在可以分配给多达128个虚拟处理器/VCPU(不过…

Xen - Networking

Xen - Networking Step 0 : Xen底下的網路架構 Step 1 : 虛擬還是實體 Step 2 : network-bridge Step 3 : network-nat Step 4 : network-route Step 5 : 參考網頁 Step 0 : Xen底下的網路架構 在虛擬化下的環境就屬網路裝置最為複雜難懂,不過模式大致可以畫分成 3 種 1.netw…

Xen虚拟化之一:Xen环境组件详解

Xen是一个开放源代码虚拟机监视器(Virtual Machine Monitor&#xff0c;简称为VMM)&#xff0c;由剑桥大学开发&#xff0c;它致力于实现在单个计算机上运行多达128个有完全功能的操作系统。Xen通过一种叫做半虚拟化(paravirtualization)的技术获得高效能的表现&#xff08;较少…

浅谈Xen和半虚拟化技术

研究生入学的时候&#xff0c;看了一篇论文——《Xen and the art of virtualization》。现在时隔一年&#xff0c;准备对此进行一番整理。下文是我Xen为例的半虚拟化技术的理解&#xff1a; 虚拟机概况 首先从虚拟机说起&#xff0c;虚拟机技术最早由IBM于上世纪六七十年代提…

xen架构

Xen是一个虚拟机监视器&#xff08;Virtual machine monitor&#xff09;&#xff0c;针对X86系列计算机设计&#xff0c;它能够支持多个客户计算机的同时运行&#xff0c;并且能够达到较好的一个性能水平和资源隔离。Xen是一个开放源代码软件&#xff0c;在GNU General Public…

Xen概述

http://my.oschina.net/davehe/blog/94039 1 Xen概述 1.1 简介 Xen是由剑桥大学计算机实验室开发的一个开源项目。是一个直接运行在计算机硬件之上的用以替代操作系统的软件层&#xff0c;它能够在计算机硬件上并发的运行多个客户操作系统&#xff08;Guest OS&#xff…

KVM和Xen虚拟化有什么区别?Xen和KVM优缺点对比

KVM和Xen是两大虚拟化技术&#xff0c;KVM和Xen又是免费开源的管理程序&#xff0c;新手站长网分享虚拟化技术KVM和Xen的区别优势对比&#xff1a; KVM和Xen的区别 KVM&#xff1a;KVM是轻量级的虚拟化管理程序模块&#xff0c;该模块主要来自Linux内核&#xff1b;KVM的虚拟…

Xen与XenServer的区别

说到XenServer&#xff0c;总是离不开Xen&#xff0c;所以我要说他们的区别&#xff0c;得首先从Xen开始说起&#xff01; Xen体系架构 Xen hypervisor体系架构 Xen 的 VMM ( Xen Hypervisor ) 位于操作系统和硬件之间&#xff0c;负责为上层运行的操作系统内核提供虚拟化的硬件…

xen的安装

一、 Xen介绍 : 在虚拟化软件的部份&#xff0c;可分为VMWare、Xen、KVM、VritualBox是较为常见的。在Xen这到自由软件上主要可分为半虚拟化(Para-virtualization) 及全虚拟化 (Full virtualization) 两种&#xff0c;其中半虚拟化主要是透过修改 Linux 核心来达成的虚拟技术。…

虚拟机体验之 Xen 篇 —— 令人脑洞大开的奇异架构

转载于https://www.cnblogs.com/youxia/p/linux022.html#_label0 阅读目录 总结&#xff1a; 这一篇我要体验的虚拟机系统是 Xen。在虚拟机领域&#xff0c;Xen 具有非常高的知名度&#xff0c;其名字经常在各类文章中出现。同时 Xen 也具有非常高的难度&#xff0c;别说玩转…

xen基础

xen结构概述 一个 Xen 虚拟化环境包括一组项目&#xff0c;它们一起工作来提供虚拟化环境&#xff1a;Xen hypervisor&#xff1b;dom0&#xff1b;domain management and control&#xff0c;域的管理和控制&#xff1b;domU PV 客户机&#xff1b;domU HVM 客户机。 它们之间…

Xen 简介

---------------------------------同样来自 IBM ------------------------------------ Xen 是一种类型 1 虚拟机管理程序&#xff0c;它创建系统资源的逻辑池&#xff0c;使许多虚拟机可共享相同的物理资源。 Xen 是一个直接在系统硬件上运行的虚拟机管理程序。Xen 在系统硬…

全面详解Python与Ruby,到底哪款更优秀

今天,我和大家讨论一下是Python开发语言web好还是Ruby开发语言web好,有需要的小伙伴,可以参考一下。对这方面有自己见解的大神,可以交流一下。希望大家可以认真阅读哦! Python 和 Ruby 都是目前用来开发 websites、web-based apps 和 web services 的流行编程语言之一。 …

Ruby入门级示例代码

【实例简介】 针对入门级的新手参考 【实例截图】 目录结构&#xff1a; 文件&#xff1a;590m.com/f/25127180-494436327-f5ef7f&#xff08;访问密码&#xff1a;551685&#xff09; 【核心代码】class ItemController < ApplicationControllerscaffold :itemdef creat…

Ruby基础教程(Day1)—— Ruby初探

前提&#xff1a;自行安装ruby 一、Ruby初探 最常见的方法是使用ruby命令执行&#xff08;在helloruby.rb中输入print("Hello,Ruby.\n")&#xff09; 在命令行输入ruby helloruby.rb irb命令&#xff0c;以交互命令行方式来执行 在控制台执行irb如下图所示 对象 …

RubyPloticus

原文&#xff1a; RubyPloticus ruby 2006年6月19日 Bliki 索引 译注&#xff1a;代码和生成的图片示例可从这里下载。 在最近的帖子“ 评估Ruby”中&#xff0c;我提到一位同事曾在一个Web应用中加入了一些漂亮的数据图表&#xff0c;有人email问我是…

Ruby(一)

Ruby 是一种开源的面向对象程序设计的服务器端脚本语言&#xff0c;可运行于多种平台&#xff0c;如 Windows、MAC OS 和 UNIX 的各种版本。Ruby流行起来的根本原因是因为基于Ruby的Web开发框架Rails的广泛使。 1、ruby环境 windows&#xff1a;Downloads (rubyinstaller.org…

Python 和 Ruby 的对比

&#xff08;点击上方公众号&#xff0c;可快速关注&#xff09; 来源&#xff1a;js信仰者 segmentfault.com/a/1190000010756033 如有好文章投稿&#xff0c;请点击 → 这里了解详情 最近在考虑学习一门后端语言&#xff0c;在ruby和python直接犹豫&#xff0c;然后自己做了…

【Python】Pyyaml和ruamel.yaml

目录 PYYAML 读取yaml 保存yaml 读取保存的yaml文件 yaml文件规则 yaml文件数据结构 ruamel.yaml 格式化保存yaml 使用ruamel.yaml读取yaml 使用ruamel.yaml时python中符号对应于yaml中符号 PYYAML config.yaml文件 username: zxx age: 18 orther:height: 175CMwei…