jinja2 通过添加自定义parser实现macro的名称调整

实际上是dbt 在设计macro 中的一个技巧,比较有意思,对于系统或者三方包开发的macro 添加自己prefix
比如,原始macro name 为dremio__alter_column_type dbt 会将解析的macro 添加一个dbt_macro__的前缀为
dbt_macro__dremio__alter_column_type,以下说明下实现

参考实现

  • 自定义解析&env
import jinja2
 
import jinja2.sandbox
import jinja2.parser
import jinja2.nodes 
# 类似dbt get_dbt_macro_name 工具方法
def get_dbt_macro_name(name):
    return f"dbt_macro__{name}"
# 自定义解析
class MacroFuzzParser(jinja2.parser.Parser):
    def parse_macro(self):
        node = jinja2.nodes.Macro(lineno=next(self.stream).lineno)
        # 解析名称添加前边的前缀
        node.name = get_dbt_macro_name(self.parse_assign_target(name_only=True).name)
 
        self.parse_signature(node)
        node.body = self.parse_statements(("name:endmacro",), drop_needle=True)
        return node
# 自定义env
class MacroFuzzEnvironment(jinja2.sandbox.SandboxedEnvironment):
    def _parse(self, source, name, filename):
        return MacroFuzzParser(self, source, name, filename).parse()
  • 使用
from jinja import MacroFuzzEnvironment,get_dbt_macro_name
 
env = MacroFuzzEnvironment()
 
macro_template =  """
{% macro mydemo(name, age) -%}
   {{name}} -----{{age}}
{%- endmacro %}
 
"""
# 通过string 模式的模版加载
template = env.from_string(macro_template)
 
context = {"name":"dalong","age":18}
 
# template.make_module 创建模块
module  = template.make_module(vars=context, shared=False)
# module 会包含一个macro 名称的属性(是一个方法)
 
print(module.__dict__)
 
macro_func  = module.__dict__[get_dbt_macro_name("mydemo")]
# 调用方法
info = macro_func(**context)
print(info)
  • 效果

说明

dbt 对于jinja2 的一些扩展比较有意思,很值得学习, 可以方便的实现一个隔离

参考资料

posted on 2024-04-14 06:46  荣锋亮  阅读(16)  评论(0编辑  收藏  举报

导航