Python set objects in Jinja?

5,670 views
Skip to first unread message

Justin Lloyd

unread,
Dec 9, 2015, 3:02:04 PM12/9/15
to Salt-users
Maybe I'm missing something obvious, but is it possible to define a Python set object in Jinja2 code? This doesn't work:

{% set files = set(['/tmp/foo', '/tmp/bar']) %}

It fails with the message "Jinja variable 'set' is undefined".

The problem I need to solve is that I need to iterate over the 'files' variable to create new files so it must be a list of unique values. FWIW, in reality I'm generating 'files' from multiple Pillar keys that could contain duplicate values.

Loren Gordon

unread,
Dec 9, 2015, 4:17:11 PM12/9/15
to Salt-users
I don't think jinja2 is able to do that. You could write the state in the pure python renderer, though. Or you could iterate over the list to compare whether the item is already in the list. But it's ugly in Jinja, because inside a loop the variables you set are not available outside the loop; to workaround that, you need to use a map and the update() function...

{% set files = [ 'foo', 'foo', 'bar', 'abc', 'bar' ] %}

{% set dummymap = {'files': []} %}
{% for file in files if file not in dummymap.files %}
    {% do dummymap.update({'files': dummymap.files + [file]}) %}
{% endfor %}

mystate:
  cmd.run:
    - name: echo {{ dummymap.files }}

-Loren

Justin Lloyd

unread,
Dec 9, 2015, 4:22:39 PM12/9/15
to salt-...@googlegroups.com
I worked around it by just wrapping an simple check whether the value is already in the list around the append call, but it's good to know I wasn't missing anything obvious. I also didn't know about the "for ... if ..." syntax, so TIL. Thanks!

Justin


--
You received this message because you are subscribed to a topic in the Google Groups "Salt-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/salt-users/fNHIXqxyieY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to salt-users+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Loren Gordon

unread,
Dec 9, 2015, 4:41:27 PM12/9/15
to Salt-users
Oh, right, append() works on lists. Don't need the map after all because the list contents persist outside the loop. And yeah, the inline if syntax is very handy. It also works with "do... if..." and "set = ... if ...". Even supports an "else" clause.

{% set files = [ 'foo', 'foo', 'bar', 'abc', 'bar' ] %}
{% set fileset = [] %}
{% for file in files if file not in fileset %}
    {% do fileset.append(file) %}
{% endfor %}

Also, fwiw, there is an open issue to support custom filters, which we could use to implement "set" functions...


-Loren

Justin Lloyd

unread,
Dec 9, 2015, 6:40:46 PM12/9/15
to salt-...@googlegroups.com
Yeah, this is much cleaner, ultimately just appending the conditional to the for statement.

{% set plugins = salt.pillar.get('collectd:plugins:default', []) %}
{% for role in grains['roles'] %}
{%   for plugin in salt.pillar.get('collectd:plugins:' + role, []) if plugin not in plugins %}
{%     do plugins.append(plugin) %}
{%   endfor %}
{% endfor %}


Thanks for the tips!



--
You received this message because you are subscribed to the Google Groups "Salt-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to salt-users+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages