Controlling whitespace in Jinja2 templates: Difference between revisions

link to project on GitHub
add example of Ansible weirdness with templates
 
(6 intermediate revisions by the same user not shown)
Line 2: Line 2:


For background perspective, '''[https://jinja.palletsprojects.com Jinja]''' is used as a templating system by various Python projects like Django, Flask, or Ansible.  
For background perspective, '''[https://jinja.palletsprojects.com Jinja]''' is used as a templating system by various Python projects like Django, Flask, or Ansible.  
=== '''Use spaces''' ===
Since Jinja is based on Python where spaces are relevant, it is preferred to use spaces in your templates rather than tabs.


From the Jinja2 documentation, there are two options to control whitespace in Jinja templates:
From the Jinja2 documentation, there are two options to control whitespace in Jinja templates:
Line 32: Line 35:
**[https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html#parameter-lstrip_blocks parameter lstrip_blocks]
**[https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html#parameter-lstrip_blocks parameter lstrip_blocks]
**[https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html#parameter-trim_blocks parameter trim_blocks]
**[https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html#parameter-trim_blocks parameter trim_blocks]
<syntaxhighlight lang="text">
[meza-ansible@rockylinux-s-4vcpu-8gb-nyc3-01 config]$ ansible --version
ansible [core 2.16.3]
  config file = /opt/meza/config/ansible.cfg
  configured module search path = ['/opt/conf-meza/users/meza-ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.12/site-packages/ansible
  ansible collection location = /opt/meza/collections
  executable location = /usr/bin/ansible
  python version = 3.12.11 (main, Jul  1 2025, 15:29:55) [GCC 8.5.0 20210514 (Red Hat 8.5.0-26)] (/usr/bin/python3.12)
  jinja version = 3.1.2
  libyaml = True
</syntaxhighlight><syntaxhighlight lang="yaml">
# Adds extensions with composer param from MezaCoreExtensions.yml,
# MezaLocalExtensions.yml, MezaCoreSkins.yml, and MezaLocalSkins.yml
- name: Ensure composer.local.json in place to load composer-based extensions
  template:
    src: composer.local.json.j2
    dest: "{{ m_mediawiki }}/composer.local.json"
    mode: "{{ m_htdocs_mode }}"
    owner: "{{ m_htdocs_owner }}"
    group: "{{ m_htdocs_group }}"
    lstrip_blocks: false
    trim_blocks: true
</syntaxhighlight>


==In the template file==
==In the template file==
As the '''very first line''' of your template file, you can use a magic comment directive to control the behavior of the Ansible Template module  
As the '''very first line''' of your template file, you can use a magic comment directive to control the behavior of the Ansible Template module  
<code>#jinja2: trim_blocks: "true", lstrip_blocks: "true"</code><ref>It's unclear whether the boolean value(s) to be quoted. I believe it was a bug that is now fixed - meaning they can be bare.</ref>
<code>#jinja2: trim_blocks: "true", lstrip_blocks: "true"</code><ref>It's unclear whether the boolean value(s) to be quoted. I believe it was a bug that is now fixed - meaning they can be bare.</ref>
Given this code in the template:
"config": {
    {%- if overwrite_local_changes %}
    "discard-changes": true
    {%- else %}
    "discard-changes": false
    {%- endif %}
}
We could not get the result from Ansible that we could get from the liver parser (and what we expected); namely that the closing brace would be on a line by itself. 
"config":•{
••••"discard-changes":•true
}
Ansible puts the closing brace on the same line as the output.
"config":•{
••••••••••••"discard-changes":•true••••}


==By hand==
==By hand==


I was so frustrated by the lack of clear examples '''that showed all variants''', I created a project on GitHub called '''[https://github.com/freephile/test-jinja test-jinja]''' that does exactly that. (Inspiration from https://github.com/ansible/ansible/pull/37478)
I was so frustrated by the lack of clear examples '''that showed all variants''', I created a project on GitHub called '''[https://github.com/freephile/test-jinja test-jinja]''' that does exactly that. [https://github.com/freephile/test-jinja/blob/main/files/manual_blocks.md TLDR]. <ref>Inspiration from https://github.com/ansible/ansible/pull/37478</ref>


You could also use the [https://github.com/qn7o/jinja2-live-parser jinja2 live parser] project to interactively preview jinja.
You could also use the [https://github.com/qn7o/jinja2-live-parser jinja2 live parser] project to interactively preview jinja. I found Ansible playbook execution to be inconsistent with the expected behavior and results I was getting from the live parser. But at least it gives you the ability to run through example template loops, conditions and blocks interactively to illustrate how the Ansible playbook ''should'' work. The short version of using the live parser is to clone the repo, build it, and docker run it.<syntaxhighlight lang="bash">
# clone it
git clone https://github.com/abourguignon/jinja2-live-parser.git
# change into the cloned source directory
cd jinja2-live-parser/
# build and tag the image
docker build -t mydocker/j2parser .
# run it on port 5000 in detached mode
docker run -d -p 5000:5000 mydocker/j2parser
</syntaxhighlight>Or, just pull and run the pre-built image from Docker hub.


== Variables ==
If you're confused about Variable notation and how those are defined in templates, see https://jinja.palletsprojects.com/en/3.1.x/templates/#variables
If you're confused about Variable notation and how those are defined in templates, see https://jinja.palletsprojects.com/en/3.1.x/templates/#variables


==Debugging==
==Debugging==
Just put <code>{% debug %}</code> somewhere in your template file. This didn't work for me in Ansible 2.9
Just put <code>{% debug %}</code> somewhere in your template file. This didn't work for me in Ansible 2.9
== More ==
Bloomreach Engagement is a commercial Web product that uses Jinja for templating. So they have a pretty good [https://documentation.bloomreach.com/engagement/docs/jinja-syntax reference guide].


== See Also ==
For whitespace formatting of YAML block scalars, see the [[yamllint]] article.


== More ==
{{References}}
Bloomreach Engagement is a commercial Web product that uses Jinja for templating. So they have a pretty good [https://documentation.bloomreach.com/engagement/docs/jinja-syntax reference guide].{{References}}


[[Category:Ansible]]
[[Category:Ansible]]
[[Category:Templating]]
[[Category:Templating]]
[[Category:Python]]
[[Category:Python]]