jinja: 2.9 regression when assigning a variable inside a loop

2.9:

>>> jinja2.Template('{% set a = -1 %}{% for x in range(5) %}[{{ a }}:{% set a = x %}{{ a }}] {% endfor %}{{ a }}').render()
u'[:0] [:1] [:2] [:3] [:4] -1'

2.8:

>>> jinja2.Template('{% set a = -1 %}{% for x in range(5) %}[{{ a }}:{% set a = x %}{{ a }}] {% endfor %}{{ a }}').render()
u'[-1:0] [0:1] [1:2] [2:3] [3:4] -1'

Originally reported on IRC:

A change in jinja2 scoping appears to affect me, and I’m unsure of the correct fix. Specifically the problem is the assignment of year here: https://github.com/kennethlove/alex-gaynor-blog-design/blob/551172/templates/archive.html#L13-L24

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 65 (48 by maintainers)

Commits related to this issue

Most upvoted comments

changed_from_last sounds good - at least functionality-wise, the name itself is a bit awkward IMO. Maybe just changed would be clear enough?

I guess the first element would always be considered “changed” no matter what it is? If that behavior is not OK for someone they could always add a not loop.first check anyway.

ok, so at least no risk of templates causing unexpected side-effects on objects passed to them.

still a bit wary of the things people might do…

{% macro do_stuff(ns) %}
    {% set ns.foo %}bar{% endset %}
    {% set ns.bar %}foobar{% endset %}
{% endmacro %}

{% set ns = namespace() %}
{{ do_stuff(ns) }}

Actually, I think a new block like {% namespace ns %} would be better to define one than a callable - a variable named namespace doesn’t sound like something very unlikely to be passed to a template, and while it would probably simply prevent you from using the namespace feature in that template (just like shadowing a builtin in Python) it feels a bit dirty…

Hi, I understand that there are a lot of “ugly” examples due to this, but could you please advise how to increment a variable on a for loop in jinja template in elegant/right way? Because my code has been broken too.

@pujan14

{{ names|map(attribute="last")|select|unique }}

Although since unique isn’t a built-in filter, you could always just add another filter that does the whole thing, since you’re already adding the unique filter.

Filter syntax would not be possible here since filters are already possible on the iterable.

A possible syntax for this that wouldn’t look to bad could be this:

{% for article in dates with last_day %}

Especially since with already does scoping stuff in Jinja when used e.g. as {% with %}. OTOH, here it would be the opposite since with blocks open a new scope, not make outer scope vars accessible…

Not sure though if that’s a feature Jinja needs or not.