ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Mako 模板引擎语法完全指南:表达式、控制结构与标签体系(Lynx 仓库视角)

Mako 模板引擎语法完全指南:表达式、控制结构与标签体系(Lynx 仓库视角) Mako 模板引擎语法完全指南表达式、控制结构与标签体系Lynx 仓库视角【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynxMako 是一个将模板编译为真实 Python 代码的模板引擎其语法参考文档被完整收录于本仓库 third_party/binding/idl-codegen/third_party/doc/build/syntax.rst配套的 HTML 渲染版本位于 third_party/binding/idl-codegen/third_party/doc/syntax.html。本文以该文档为骨架系统讲解 Mako 的表达式替换、转义、控制结构、注释、Python 代码块以及%标签全家桶并结合仓库内真实的模板驱动代码生成实践如 third_party/binding/idl-codegen/templates 与 tools/config说明如何把模板语法落地为批量产出源码的生产力工具。读完本文你将能够读懂并编写任何 Mako 模板——从一行${x}到复杂的继承链与命名空间标签。文档在仓库中的定位本仓库的 third_party/binding/idl-codegen/README.md 说明idl-codegen目录整体取自 Chromium 90.0.4430.71 标签下的 IDL 代码生成器其中third_party目录用于存放任何被引用的第三方内容。syntax.rst正是随该工具链一并引入的Mako 模板语言官方语法参考与usage.rst、filtering.rst、defs.rst、namespaces.rst、inheritance.rst、caching.rst、runtime.rst、changelog.rst共同构成一套完整的 Mako 使用文档。Mako 的核心设计思想在文档开头即被点明模板从任意文本流XML、HTML、邮件文本等解析而来其中的 Mako 指令变量/表达式替换、控制结构、服务端注释、整段 Python 代码、各类功能标签最终全部编译为真实的 Python 代码。这意味着几乎可以在模板的每一个角落调用完整的 Python 能力——这也是它能胜任大规模代码生成任务的根本原因。表达式替换Expression Substitution最简单的表达式就是变量替换语法为${}该形式借鉴自 Perl、Genshi、JSP EL 等语言this is x: ${x}上例中x的字符串表示会被写入模板的输出流。x通常来自渲染模板时提供的Context对象如果模板没有收到x、本地也没有赋值表达式会求值为一个特殊值UNDEFINED详见 runtime.rst 中关于Context的说明。${}内部由 Python 直接求值因此可以写完整的表达式pythagorean theorem: ${pow(x,2) pow(y,2)}所有表达式结果在渲染到输出流之前都会先被转成字符串——上面的例子中表达式产生的是数值结果同样适用。表达式转义Expression EscapingMako 内置了多种转义机制HTML 转义、URI 转义、XML 转义以及一个trim修剪函数。转义通过管道操作符|附加到表达式替换上${this is some text | u}该表达式对结果做 URL 转义输出thisissometext。内置过滤器名称及其含义如下表过滤器名作用示例输出uURL 转义this is some text→thisissometexthHTML 转义对、、等字符进行 HTML 实体转义xXML 转义对 XML 特殊字符进行转义trim去除首尾空白对表达式结果应用 trim 函数关于内置过滤函数的更多细节以及如何编写自定义过滤器filter function见 filtering.rst。控制结构Control Structures控制结构指所有控制程序流程的语法条件if/else、循环while、for以及try/except等。在 Mako 中控制结构用%标记开头后跟一条常规的 Python 控制表达式并以另一个%标记加上endname闭合其中name是表达式的关键字% if x5: this is some output % endif%可以出现在行首之后的任意位置只要该行行首没有其他文本缩进并不重要。这里可以使用完整的 Python 冒号表达式包括if/elif/else、while、for、with甚至def——尽管 Mako 为 def 提供了功能更完备的内置标签% 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循环上下文The Loop Context循环上下文为% for结构内部提供关于当前循环的额外信息自Mako 0.7版本加入ul % for a in (one, two, three): liItem ${loop.index}: ${a}/li % endfor /ulloop.index只是loop对象的众多属性之一完整能力见 runtime.rst 中对LoopContext的说明。注释CommentsMako 提供两种注释。单行注释使用##作为一行的前两个非空白字符## this is a comment. ...text ...多行注释使用%doc ...text... /%doc%doc these are comments more comments /%doc换行过滤器Newline Filters反斜杠\放在行尾时会吞掉换行符后继续到下一行here is a line that goes onto \ another line.上面的文本求值结果为here is a line that goes onto another line.Python 代码块Python Blocks任意一段 Python 代码都可以用% %标签插入模板this is a template % x db.get_resource(foo) y [z.element for z in x if x.frobnizzle5] % % for elem in y: element: ${elem} % endfor% %内部就是一段常规的 Python 代码。代码前的空白层级可以任意但内部必须自洽统一Mako 编译器会自动调整这段 Python 的缩进使其与周边生成的 Python 代码保持一致。模块级代码块Module-level Blocks% %的变体是模块级代码块记作%! %。其中的代码在模板的模块层级执行而非模板的渲染函数内因此无法访问模板的Context只在模板被加载进内存时执行一次每个应用一次或取决于运行时环境的更多次。%! %用于声明模板的 import 以及纯 Python 辅助函数%! import mylib import re def filter(text): return re.sub(r^, , text) %模板中可以在任意位置声明任意数量的%! %块它们会按在源模板中出现的顺序在生成的模块里所有渲染可调用对象之上合并为一段连续代码块输出。标签体系TagsMako 其余的能力都以标签形式提供。所有标签使用同一套语法——类似 XML 标签但标签名的第一个字符是%。标签可以用自闭合斜杠关闭也可以用显式的闭合标签%include filefoo.txt/ %def namefoo bufferedTrue this is a def /%def每个标签都定义了一组属性其中部分为必填。许多属性还支持求值evaluation即可以在属性文本中嵌入${}表达式%include file/foo/bar/${myfile}.txt/属性是否接受运行时求值取决于标签类型及其编译进模板的方式。最直接的验证方式就是动手试——如果不合法词法分析器lexer会明确报错。%page模板的总体特征该标签定义模板的总体特征包括缓存参数以及模板被调用时期望接收的可选参数列表%page argsx, y, zdefault/或定义缓存特征%page cachedTrue cache_typememory/当前每个模板只会使用一个%page标签其余被忽略文档注明该行为将在未来版本改进因此现阶段请确保模板中只定义一个%page标签。其具体用途分散在多个章节namespaces.rst定义模板级参数与默认值filtering.rst通过%page为模板内所有表达式统一应用过滤器caching.rst模板级缓存选项。%include文件包含%include是其他模板语言中常见的常规标签仅接受一个 file 参数并调用被包含文件的渲染结果%include fileheader.html/ hello world %include filefooter.html/include也接受参数这些参数在接收模板中以%page参数的形式可用%include filetoolbar.html argscurrent_sectionmembers, usernameed/%def模板函数%def定义一段包含内容的 Python 函数可在模板的其他位置调用%def namemyfunc(x) this is myfunc, x is ${x} /%def ${myfunc(7)}%def远比普通 Pythondef强大Mako 编译器为%def提供了许多额外服务包括将 def 导出为模板方法、自动传播当前Context、buffering/filtering/caching 标志以及带内容的 def 调用可以把一组 def 作为参数传给另一个 def 调用。全部能力见 defs.rst。%block即时执行的匿名块%block与%def相近但它会在其最基础的作用域中立即执行并且可以是匿名的不带名字——自Mako 0.4.1版本加入%block filterh some html stuff. /%block受 Jinja2 的 block 启发具名 block 为模板继承提供了一种语法上很优雅的写法html body %block nameheader h2%block nametitle//h2 /%block ${self.body()} /body /htmlBlock 的引入见 defs.rst进一步说明见 inheritance.rst。%namespace模板的 import%namespace相当于 Python 的import语句允许访问其他模板文件、纯 Python 模块的渲染函数与元数据以及本地定义的函数包%namespace filefunctions.html import*/%namespace底层生成的是 runtime.rst 中的mako.runtime.Namespace实例这是模板中引用当前 URI、继承结构等模板专属信息的中枢构造。命名空间的完整描述见 namespaces.rst。%inherit继承链%inherit让模板可以组织成继承链这是许多模板语言共有的概念%inherit filebase.html/使用%inherit后控制权首先交给最顶层的被继承模板由它决定如何处理来自继承模板的内容区。Mako 在此提供很高的灵活性包括动态继承、内容包装content wrapping和多态方法调用详见 inheritance.rst。%nsname:defname自定义标签针对某个命名空间可以用%命名空间名:def名的形式创建任何用户自定义标签。其闭合格式等价于内联表达式开放格式等价于%call标签——自Mako 0.2.3版本加入%mynamespace:somedef paramsome value this is the body /%mynamespace:somedef要创建接受 body 的自定义标签见 defs.rst 中的defs_with_content章节。%call经典的自定义标签形式call标签是用户自定义标签的经典形式与上述%namespacename:defname语法大致等价同样在 defs.rst 的defs_with_content章节中说明。%doc多行注释标签%doc标签处理多行注释%doc these are comments more comments /%doc同时行首非空白字符处使用##也可以进行单行注释。%text暂停解析输出原文%text标签会暂停 Mako 词法分析器对模板指令的正常解析把整个 body 原样返回为纯文本——常用于撰写关于 Mako 本身的文档%text filterh heres some fake mako ${syntax} %def namex()${x}/%def /%text从模板中提前退出Exiting Early from a Template有时你希望在模板或%def方法执行到一半时停止处理只使用目前已累积的文本。方法是在 Python 代码块中执行return语句。建议return返回空字符串避免 Python 默认的None返回值被模板渲染出来。模板中通过STOP_RENDERING符号语义化地表达这一空字符串返回值自Mako 1.0.2加入% if not len(records): No records found. % return STOP_RENDERING % % endif或者% if not len(records): return STOP_RENDERING %在 Mako 旧版本中可以用空字符串替代STOP_RENDERING% return %仓库中的模板驱动代码生成实践理解了 Mako 的语法骨架后再看本仓库中的真实代码生成管线会更有收获。虽然仓库的 IDL 绑定模板采用的是同属一个语法家族的 Jinja2 风格{% macro %}、{% if %}、{{ }}但核心思路与 Mako 完全一致用模板语言批量生成重复的、结构化的源码。以 third_party/binding/idl-codegen/templates/macros.tmpl 为例它用{% macro %}定义了一组参数转换宏配合 templates 目录下的napi_interface.cc.tmpl、napi_dictionary.h.tmpl等 16 个模板把 Web IDL 定义翻译为 N-API 绑定代码——宏内大量使用{% if argument.enum_type %}、{% for value in argument.enum_values %}这类控制结构与 Mako 的% if/% for一一对应{{argument.index}}表达式替换也对应 Mako 的${}。这意味着读懂本文的语法就足以看懂仓库里任何模板文件的生成逻辑。另一处典型实践是配置代码生成器 tools/config/gen_config.py它通过from jinja2 import Template读取 YAML 配置源CONFIG_YAML_PATH再以 tools/config/templates/config_keys.tmpl 等模板批量产出 TypeScript/文档等产物。例如config_keys.tmpl中{% for option in options %} {% if not option.deprecated and (not export or (export and option.export))%} {{option.name}}, {% endif %} {% endfor %}这种数据文件 模板 多形态产物的模式正是 Mako 文档所倡导的模板编译为真实代码、充分复用 Python 能力在工程上的直接体现。结语syntax.rst用约五百行篇幅覆盖了 Mako 的全部核心语法从${}表达式替换、|转义管线、%控制结构、loop循环上下文到##/%doc注释、% %/%! %两类 Python 块再到%page、%include、%def、%block、%namespace、%inherit、%call、%text等一整套标签以及STOP_RENDERING提前退出机制。配合 usage.rstAPI 使用、defs.rstdef 进阶、namespaces.rst命名空间、inheritance.rst继承、filtering.rst过滤与 caching.rst缓存等姊妹篇即可构建完整的 Mako 知识体系。而仓库自身的模板驱动代码生成管线则为这套语法提供了最直观的落地参照。【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表