Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using {{ caller() }} within {% call %} fails #371

Closed
Xion opened this issue Sep 23, 2014 · 5 comments
Closed

Using {{ caller() }} within {% call %} fails #371

Xion opened this issue Sep 23, 2014 · 5 comments

Comments

@Xion
Copy link

Xion commented Sep 23, 2014

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.

@benallard
Copy link

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

@benallard
Copy link

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

@mitsuhiko
Copy link
Contributor

mitsuhiko commented Jan 7, 2017

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

@ThiefMaster
Copy link
Member

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.

@mitsuhiko
Copy link
Contributor

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants