_O modelo real onde encontrei o problema primeiro era mais clichê, mas este descreve o problema de uma maneira mais intuitiva._
Suponha que temos o seguinte modelo:
{%- macro _tag(tag, id=none, classes=(), attrs={}) -%}
<{{ tag }}
{% if id %} id="{{ id }}" {% endif %}
{% if classes %} class="{{ classes|join(' ') }}" {% endif %}
{{ attrs|xmlattr }}>
{% if caller %} {{ caller() }} {% endif %}
</{{ tag }}>
{%- endmacro -%}
juntamente com um modelo de nível superior que tenta {% call %}
para algum caso específico:
{%- macro _div() -%}
{% call _tag('div', *varargs, **kwargs) %}
{% autoescape false %}
{% if caller %} {{ caller() }} {% endif %}
{% endautoescape %}
{% endcall %}
{%- endmacro -%}
Intuitivamente, fazer {% call _div(...) %} <b>foo</b> {% endcall %}
deve passar <b>foo</b>
para a macro _tag
, que então a renderiza (literalmente, graças ao autoescape). Mas o resultado real é UndefinedError: No caller defined
na linha {{ caller() }}
dentro _div
. Parece que o escopo de caller
não se estende ao bloco {% call %}
, evitando assim chamadas encadeadas como a mostrada acima.
Existe uma solução feia que usa o novo recurso de atribuição de bloco do 2.8:
{%- macro _div() -%}
{% set content %}
{% if caller %} {{ caller() }} {% endif %}
{% endset %}
{% call _tag('div', *varargs, **kwargs) %}
{% autoescape false %} {{ content }} {% endautoescape %}
{% endcall %}
{%- endmacro -%}
mas, claro, a versão original seria muito mais preferível.
Eu sou capaz de reproduzir isso com este pequeno exemplo:
{% macro a() %}
start of a
{% call b() %}
{{ caller() }}
{% endcall %}
end of a
{% endmacro %}
{% macro b() %}
start of b
{{ caller() }}
end of b
{% endmacro %}
{% call b() %}
inside b only
{% endcall %}
{#
{% call a() %}
inside a
{% endcall %}
#}
Isso funciona bem como está, quando o último bloco é descomentado, obtemos o seguinte traceback:
Traceback (most recent call last):
...
File "/usr/lib/python2.7/dist-packages/jinja2/environment.py", line 894, in render
return self.environment.handle_exception(exc_info, True)
File "templates/test.j2", line 19, in top-level template code
{% call a() %}
File "templates/test.j2", line 3, in template
{% call b() %}
File "templates/test.j2", line 11, in template
{{ caller() }}
File "templates/test.j2", line 4, in template
{{ caller() }}
jinja2.exceptions.UndefinedError: No caller defined
Pequena observação sobre a solução alternativa:
O novo recurso do 2.8 não é necessário, pois também está funcionando bem:
{% macro a() %}
start of a
{% set content=caller() %}
{% call b() %}
{{ content }}
{% endcall %}
end of a
{% endmacro %}
{% macro b() %}
start of b
{{ caller() }}
end of b
{% endmacro %}
{% call b() %}
inside b only
{% endcall %}
{% call a() %}
inside a
{% endcall %}
Isso é meio que intencional, pois sempre abrange a macro mais próxima. Fechando como não vai corrigir.
Para sua informação, {% set caller_ = caller %}
também funciona bem, então pode ser chamado sempre que necessário.
De qualquer forma, isso pode valer a pena uma entrada de FAQ. Lembro-me de um colega tendo exatamente o mesmo problema algum tempo atrás, até descobrirmos que {% set caller_ = caller %}
antes de entrar em outro bloco de chamadas ajuda.
Sim, podemos querer colocar isso nos documentos.
Comentários muito úteis
Pequena observação sobre a solução alternativa:
O novo recurso do 2.8 não é necessário, pois também está funcionando bem: