How can I print a jinja dict in a deterministic order?
Question: I have a configuration file where a particular setting takes a long ugly json-
style dictionary. I would like to define this dictionary in YAML and print it
out with Jinja. Currently this looks something like this:

    
    
    {%- load_yaml as some_conf_val %}
    val1: something
    val2: completely
    val3: different
    {%- endload %}
    
    option = {{ some_conf_val }}
    
    # Results in:
    option = {'val2': 'completely', 'val1': 'something', 'val3': 'different'}
    

By happy coincidence this is exactly the format expected by the program being
configured, and the yaml block is much easier to read and modify than the
inline version. However, the fact that the keys come out in neither
alphabetical order nor the order they were defined leads me to suspect their
output order is non-deterministic. On re-running the state a few times they
always come out in the same order, but that doesn't prove much.

This is a problem because if the output string is altered, it gets treated as
a change to the file and triggers a service restart, even if nothing
functional has been changed. I don't care what specific order the keys are in,
but I do need that order to be the same every time.

How can I accomplish this? Or is it already deterministic and just doesn't
look like it?

(If I understand correctly jinja dicts are really python dicts under the hood,
and python dicts are unordered, so this may be impossible without including
code that's messier than the line I'm trying to not have to write. But I'm
hoping not.)


Answer: You are right, Python's standard dict is unordered. There is an ordered
version in the `collections` module:
[`collections.OrderedDict`](https://docs.python.org/3.4/library/collections.html#collections.OrderedDict).
We can use it for this purpose with a custom Jinja2 filter.

The custom filter first converts the input to OrderedDict, then to JSON, and
finally replaces the double quotation marks:

    
    
    from json import dumps
    from collections import OrderedDict
    
    def conffilter(value):
        return dumps(OrderedDict(value)).replace('"', '\'')
    

Then we have to [add this new
filter](http://jinja.pocoo.org/docs/dev/api/#custom-filters) to Jinja2:

    
    
     env.filters['conffilter'] = conffilter
    

where `env` is the Jinja2
[`Environment`](http://jinja.pocoo.org/docs/dev/api/#basics) object from your
app (e.g. in Flask it's the `app.jinja_env`).

Now we can use the new filter with Jinja2's
[`dictsort`](http://jinja.pocoo.org/docs/dev/templates/#dictsort) filter.

    
    
    # Define a list for testing:
    {%- set some_conf_val = {'val2': 'completely', 'val1': 'something', 'val3': 'different'} -%}
    
    # Unordered dict output
    option = {{ some_conf_val }}
    # Result:
    option = {'val1': 'something', 'val3': 'different', 'val2': 'completely'}
    
    # Ordered list of tuples
    option = {{ some_conf_val|dictsort }}
    # Result:
    option = [('val1', 'something'), ('val2', 'completely'), ('val3', 'different')] 
    
    # Ordered dict-like output:
    option = {{ some_conf_val|dictsort|conffilter }}
    # Result:
    option = {'val1': 'something', 'val2': 'completely', 'val3': 'different'} 
    

