Search code examples
pythonansiblejinja2

Testing ansible "to_nice_json" filter within Python - "No filter named 'to_nice_json'"


I want to test an Ansible Jinja2 template using a Python script, based on the answer provided here: How can I test jinja2 templates in ansible?

This code used to work, however I don't remember in which environment. Now, when I run it, I get an error about the filter not being found:

#!/usr/bin/env bash

# python3 -m pip install ansible

python3 <<EOF
import ansible
import jinja2

print(ansible.__version__)
print(jinja2.__version__)

output = jinja2.Template("Hello {{ var | to_nice_json }}!").render(var=f"{{ 'a': 1, 'b': 2 }}")
print(output)
EOF

This returns:

2.15.6
3.1.2
Traceback (most recent call last):
  File "<stdin>", line 7, in <module>
  File "/Users/werner/.pyenv/versions/3.11.6/lib/python3.11/site-packages/jinja2/environment.py", line 1208, in __new__
    return env.from_string(source, template_class=cls)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/werner/.pyenv/versions/3.11.6/lib/python3.11/site-packages/jinja2/environment.py", line 1105, in from_string
    return cls.from_code(self, self.compile(source), gs, None)
                               ^^^^^^^^^^^^^^^^^^^^
  File "/Users/werner/.pyenv/versions/3.11.6/lib/python3.11/site-packages/jinja2/environment.py", line 768, in compile
    self.handle_exception(source=source_hint)
  File "/Users/werner/.pyenv/versions/3.11.6/lib/python3.11/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "<unknown>", line 1, in template
jinja2.exceptions.TemplateAssertionError: No filter named 'to_nice_json'.

The jinja2 pip dependency is pulled in via ansible.

How would I make my Python code find the right filter, or get jinja2 to load the filter?


Solution

  • The solution is to load the filter explicitly into the jinja2 environment:

    import ansible
    import jinja2
    from ansible.plugins.filter.core import to_nice_json
    
    print(ansible.__version__)
    print(jinja2.__version__)
    
    env = jinja2.Environment()
    
    # Add the Ansible specific filter to the Jinja2 environment
    env.filters['to_nice_json'] = to_nice_json
    
    template = env.from_string("Hello {{ var | to_nice_json }}!")
    output = template.render(var=f"{{ 'a': 1, 'b': 2 }}")
    print(output)
    

    This prints:

    Hello "{ 'a': 1, 'b': 2 }"!
    

    I am not sure why this worked before without all that extra loading.