1

The background is AWS using AWS SAM templates. It's not possible to reuse template fragments like alarm definitions for canary deployments of lambda functions resulting in very long template files. My intention is to write a template engine as wrapper around the SAM templates.

After here a very simple example what I'm trying to do:

import os
from jinja2 import Environment, BaseLoader
from yaml import dump

def to_yaml(data):
    return dump(data)

yaml = """
model:
  data:
    message: hello world
    list_data:
      {{ model.list_data | to_yaml | indent }}
"""

model = {
    'list_data': [1, 2, 3, 4]
}

environment = Environment(loader=BaseLoader())
environment.filters.update({'to_yaml': to_yaml})
template = environment.from_string(yaml)
print(template.render(model=model))

The result is following:

model:
  data:
    message: hello world
    list_data:
      - 1
    - 2
    - 3
    - 4

The problem I have:

  • the indentation is wrong
  • (nice to have) would be great when the to_yaml could handle the indentation itself

Any help is appreciated

I have found similar articles (from naming) but they were about reading and manipulating the YAML document without jinja2 which is not my intention.

Edit: The proposed solution with indent(6) was right, however I don't like the indentation of the {{ ... }} to be on first column. In another article I found the solution that PyYAML requires an own dumper to handle the solution of YAML correctly:

class MyDumper(Dumper):
    def increase_indent(self, flow=False, indentless=False):
        return super(MyDumper, self).increase_indent(flow, False)

def to_yaml(data):
    return dump(data, Dumper=MyDumper)

yaml = """
model:
  data:
    message: hello world
    list_data:
      {{ model.list_data | to_yaml | indent(6) }}
    dict_data:
      {{ model.dict_data | to_yaml | indent(6) }}
"""

model = {
    'list_data': [1, 2, 3, 4],
    'dict_data': {'a': [1, 2], 'b': [3, 4]}
}

environment = Environment(loader=BaseLoader())
environment.filters.update({'to_yaml': to_yaml})
template = environment.from_string(yaml)
print(template.render(model=model))

The result is then:

model:
  data:
    message: hello world
    list_data:
      - 1
      - 2
      - 3
      - 4

    dict_data:
      a:
        - 1
        - 2
      b:
        - 3
        - 4

1 Answers1

1

You can fairly easily force all the indents for the list to be the same by changing it to

yaml = """
model:
  data:
    message: hello world
    list_data:
{{ model.list_data | to_yaml | indent(6, true) }}
"""

Which will make all the list elements indent to the same level. Specifically, the true in the indent() filter will make it also apply the indentation formatting to the first element, and I removed the preceding spaces from that line as well. That at least will let you manually specify the indentation for all list elements.

I'm not sure how to make it auto-determine the level of indentation, however. There also seems to be a to_nice_yaml filter mentioned in this question that is available as a plugin that you could look into, but I can't quite tell the difference it's supposed to make in the output.

fyrepenguin
  • 177
  • 1
  • 4