dbt return macro 内部实现简单说明

jinja2 默认是没有return macro 的,dbt 在实现的时候比较有意思,通过一个exception 触发的,以下是简单说明

参考使用

  • 一个包含return 的macro
{% macro demoapp(name,version) %}
   {%  if version =='v1' %}
       {{return("appdemo") }}
    {% else %}
        {{return("dalongdemo")}}
    {% endif %}
    {{name }} --  {{version}}
{% endmacro  %}
  • 简单说明
    以上如果version 为v1 会返回appdemo ,否则为dalongdemo,后边部分是不会执行的,一个使用生成效果
{{ config(materialized='table') }}
 
select * from  {{demoapp('dalong','v1')}}

编译sql

dbt 内部实现简单说明

实际上return 为dbt 通过context 传递为jinja2 模版引擎的,return 实现是一个exception,这样执行就异常了,但是dbt 自己处理了异常

  • context 变量定义
@contextmember("return")
    @staticmethod
    def _return(data: Any) -> NoReturn:
        """The `return` function can be used in macros to return data to the
        caller. The type of the data (`dict`, `list`, `int`, etc) will be
        preserved through the return call.
 
        :param data: The data to return to the caller
 
 
        > macros/example.sql:
 
            {% macro get_data() %}
              {{ return([1,2,3]) }}
            {% endmacro %}
 
        > models/my_model.sql:
 
            select
              -- getdata() returns a list!
              {% for i in getdata() %}
                {{ i }}
                {% if not loop.last %},{% endif %}
              {% endfor %}
 
        """
        raise MacroReturn(data)
  • MacroReturn 实现
class MacroReturn(DbtBaseException):
    """This is how we return a value from a macro, not an exception.
 
    Hack of all hacks
    """
 
    def __init__(self, value) -> None:
        self.value = value
  • 调用部分
    dbt_common/clients/jinja.py BaseMacroGenerator 类
def call_macro(self, *args, **kwargs):
    # called from __call__ methods
    if self.context is None:
        raise DbtInternalError("Context is still None in call_macro!")
    assert self.context is not None
 
    macro = self.get_macro()
 
    with self.exception_handler():
        try:
            return macro(*args, **kwargs)
        except MacroReturn as e:
            return e.value

说明

dbt 对于renturn macro 的实现比较有意思,实际上是通过raise exception 实现的,这样jinja2的模版就具有了特定条件返回执行的能力了

参考资料

dbt_common/clients/jinja.py
dbt_common/exceptions/macros.py
core/dbt/context/base.py

posted on 2024-03-26 15:24  荣锋亮  阅读(6)  评论(0编辑  收藏  举报

导航