69

How are extra args & kwargs handled for a Jinja2 macro? The documentation isn't exactly clear offhand.

For example, this is clearly wrong:

{% macro example_1(one, two, **kwargs) %}
    do macro stuff
{% endmacro %}

which results in

jinja2.exceptions.TemplateSyntaxError

TemplateSyntaxError: expected token 'name', got '**'

The documentation says:

kwargs

Like varargs but for keyword arguments. All unconsumed keyword arguments are stored in this special variable.

Unfortunately, any combo of extra keyword arguments is an error,

{% macro example_2(one, two) %}
    do macro stuff
{% endmacro %}

{{ example_2(one, two, test='test') }}

TypeError: macro 'example_2' takes no keyword keyword argument 'test'

I have no examples and am not poking about in the Jinja2 source code atm. The documentation isn't clear to me at this point. Any thoughts appreciated.

Community
  • 1
  • 1
blueblank
  • 4,724
  • 9
  • 48
  • 73
  • Sounds like it might not support arbitrary keyword arguments. Perhaps e.g. `{% macro example_2(one, two, test=None) %}` or similar? – Chris Morgan Dec 19 '12 at 02:54

2 Answers2

84

The trick is that kwargs has to be accessed at least once in any macro that should accept them. That is to say, you must call {{ kwargs }} once in macro body without declaring it in macro argument list. The same is true for {{ varargs }}.

This will not work

{% macro example_2(one, two) %}
    * {{one}} - {{two}}
{% endmacro %}
{{example_2(1, 2, test="Hello")}}

This will

{% macro example_2(one, two) %}
    * {{one}} - {{two}}
    * {{kwargs}}
{% endmacro %}
{{example_2(1, 2, test="Hello")}}
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • 78
    Wow, that is a very distasteful design choice. – BrenBarn Dec 19 '12 at 03:06
  • 3
    @BrenBarn - I have to agree with you - there is a *lot* of awesome stuff in Jinja - the magical pseudo-globals are not of that category. – Sean Vieira Dec 19 '12 at 03:08
  • 3
    I won't claim this is *great*, but at least we can put it in a comment to get stripped out at render time `{# {{kwargs}} #}` – Taylor D. Edmiston Apr 15 '16 at 17:59
  • 1
    The comment approach didn't work for me. Might be version specific. – Jakob Simon-Gaarde Jun 09 '16 at 16:32
  • 5
    Do you mean I'm supposed to write `{% if False %}{{ kwargs }}{% endif %}` or the version Taylor wrote? Oh... – hyperknot Nov 19 '16 at 00:22
  • @SeanVieira What do you mean by: **kwargs has to be accessed at least once** ? Could you please explain more ? – 4m1nh4j1 Jul 01 '17 at 16:04
  • @4m1nh4j1 - if you look at the *second* code snippet in my answer you'll see that I'm **using** `kwargs` - that lets me call `example_2` with "extra" arguments and not have it blow up. The *first* code snippet does not do anything with `kwargs` and so blows up when I pass it extra arguments. Does that make sense? – Sean Vieira Jul 02 '17 at 02:33
  • For those who the `* {{ kwargs }}` is **showing in the browser view**, this might be helpful: You can put `* {{ kwargs }}` in an element which you can then give a **style of display none** e.g `* {{ kwargs }} # for bootstrap` OR `* {{ kwargs }}` – Eric O. Dec 19 '18 at 13:01
  • This `{{lwargs}}`, is it acting like a `**kwargs` works cause I have a similar situation that involves passing in much variables to be used in the template @seanvieira? – George Udosen Mar 07 '20 at 05:54
0

Just to add to Sean Viera's answer, the kwargs object will be a dictionary so if you want to use it you can iterate over it by accessing the items() method, like in normal Python:

{% macro iterate_kwargs() %}
    {% for key, value in kwargs.items() %}
    The key, {{ key }}, has a value of {{ value }}
    {%- endfor -%}
{% endmacro %}

{{ iterate_kwargs(id=123, colour="blue", size="S") }}

This will output the following:

    The key, id, has a value of 123
    The key, colour, has a value of blue
    The key, size, has a value of S

This was written and tested using dbt so may not be available to all tools that use Jinja

Bilbottom
  • 725
  • 8
  • 12