Ansible Hall of Shame: Jinja Loop Counter
Someone was working with a Jinja template that they hadn’t created and having difficulty understanding it, so they brought it to the #ansible IRC channel and I hated it so much I had to preserve it for posterity.
A slightly simplified version:
{% set count = [1] %}
{% for item in ['a', 'b', 'c'] %}
{% for node in ['d', 'e'] %}
{{ node }}:{{ item }} {{ count | regex_replace('\[', '') | regex_replace('\]', '') }}
{% if count.append(count.pop() + 1) %}{% endif %}
{% endfor %}
{% endfor %}
This produces:
d:a 1
e:a 2
d:b 3
e:b 4
d:c 5
e:c 6
While storing a count in a single-element list isn’t as silly as it
first looks, since it allows you to work around Jinja’s scoping rules
and set a value from inside a loop, using regex_replace()
twice to
munge the string representation of the list is a bad choice. Even just
using replace()
twice would be better, if you don’t remember that
you can directly access the element via count[0]
.
As long as you have a reasonably recent version of Jinja, a namespace object is a better way to solve this:
{% set ns = namespace(count=1) %}
{% for item in ['a', 'b', 'c'] %}
{% for node in ['d', 'e'] %}
{{ node }}:{{ item }} {{ ns.count }}
{% set ns.count = ns.count + 1 %}
{% endfor %}
{% endfor %}