Jinja: Using {{ caller() }} within {% call %} fails

Created on 23 Sep 2014  ·  5Comments  ·  Source: pallets/jinja

_The actual template where I encountered the issue first was more boilerplate-ish, but I this one describes the problem in a more intuitive way._

Suppose we have the following template:

{%- 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 -%}

along with a higher level template that tries to {% call %} it for some specific case:

{%- macro _div() -%}
  {% call _tag('div', *varargs, **kwargs) %}
    {% autoescape false %}
      {% if caller %} {{ caller() }} {% endif %}
    {% endautoescape %}
  {% endcall %}
{%- endmacro -%}

Intuitively, doing {% call _div(...) %} <b>foo</b> {% endcall %} should pass <b>foo</b> to the _tag macro, which would then render it (verbatim, thanks to autoescape). But the actual result is UndefinedError: No caller defined on the {{ caller() }} line inside _div. It would seem that the scope of caller does not extend into the {% call %} block, thereby preventing chained calls such as the one depicted above.

There is an ugly workaround that uses the new block assignment feature from 2.8:

{%- macro _div() -%}
  {% set content %}
    {% if caller %} {{ caller() }} {% endif %}
  {% endset %}
  {% call _tag('div', *varargs, **kwargs) %}
    {% autoescape false %} {{ content }} {% endautoescape %}
  {% endcall %}
{%- endmacro -%}

but, of course, the original version would be much more preferable.

Most helpful comment

Small note about the workaround:

The new feature of 2.8 is not needed as this is also working fine:

{% 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 %}

All 5 comments

I'm able to reproduce this with this small example:

{% 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 %}
#}

This works fine as-is, when the last block is uncommented, we get the following 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

Small note about the workaround:

The new feature of 2.8 is not needed as this is also working fine:

{% 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 %}

This is sort of intentional as it always scopes to the closest macro. Closing as wontfix.

FYI, {% set caller_ = caller %} also works fine, then it can be called whenever it's needed.

Anyway, this might be worth a FAQ entry. I remember a colleague having the exact same problem some time ago until we figured out that {% set caller_ = caller %} before going into another call block helps.

Yeah, we might want to put that into the docs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DriverX picture DriverX  ·  4Comments

harobed picture harobed  ·  6Comments

guettli picture guettli  ·  5Comments

Yannik picture Yannik  ·  4Comments

mitsuhiko picture mitsuhiko  ·  3Comments