Jinja2 templates leverage Python data types and standard functions. This
makes for a rich set of operations that can be performed on data. However,
this also means that certain specifics of the underlying Python becomes
visible to template authors. Since Ansible playbooks use Jinja2 for templates
and variables, this means that playbook authors need to be aware of these
specifics as well.

Unless otherwise noted, these differences are only of interest when running
Ansible in Python2 versus Python3. Changes within Python2 and Python3 are
generally small enough that they are not visible at the jinja2 level.

In Python2, the dict.keys(), dict.values(), and dict.items()
methods returns a list. Jinja2 returns that to Ansible via a string
representation that Ansible can turn back into a list. In Python3, those
methods return a dictionary view object. The
string representation that Jinja2 returns for dictionary views cannot be parsed back
into a list by Ansible. It is, however, easy to make this portable by
using the list filter whenever using dict.keys(),
dict.values(), or dict.items():

vars:hosts:testhost1:127.0.0.2testhost2:127.0.0.3tasks:-debug:msg:'{{item}}'# Only works with Python 2#loop: "{{hosts.keys()}}"# Works with both Python 2 and Python 3loop:"{{hosts.keys()|list}}"