diff --git a/changelogs/fragments/52158-jinja2-native-types-contructed-keyed-groups.yaml b/changelogs/fragments/52158-jinja2-native-types-contructed-keyed-groups.yaml new file mode 100644 index 00000000000..119fc7c50f0 --- /dev/null +++ b/changelogs/fragments/52158-jinja2-native-types-contructed-keyed-groups.yaml @@ -0,0 +1,2 @@ +bugfixes: + - Fix unexpected error when using Jinja2 native types with non-strict constructed keyed_groups (https://github.com/ansible/ansible/issues/52158). diff --git a/lib/ansible/template/native_helpers.py b/lib/ansible/template/native_helpers.py index f9d2537d9f3..5c93ac9fec2 100644 --- a/lib/ansible/template/native_helpers.py +++ b/lib/ansible/template/native_helpers.py @@ -12,6 +12,7 @@ import types from jinja2._compat import text_type +from jinja2.runtime import StrictUndefined from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode @@ -37,6 +38,17 @@ def ansible_native_concat(nodes): if isinstance(out, AnsibleVaultEncryptedUnicode): return out.data + if isinstance(out, StrictUndefined): + # A hack to raise proper UndefinedError/AnsibleUndefinedVariable exception. + # We need to access the AnsibleUndefined(StrictUndefined) object by either of the following: + # __iter__, __str__, __len__, __nonzero__, __eq__, __ne__, __bool__, __hash__ + # to actually raise the exception. + # (see Jinja2 source of StrictUndefined to get up to date info) + # Otherwise the undefined error would be raised on the next access which might not be properly handled. + # See https://github.com/ansible/ansible/issues/52158 + # We do that only here because it is taken care of by text_type() in the else block below already. + str(out) + # short circuit literal_eval when possible if not isinstance(out, list): return out diff --git a/test/units/template/test_native_concat.py b/test/units/template/test_native_concat.py new file mode 100644 index 00000000000..db85a73b92b --- /dev/null +++ b/test/units/template/test_native_concat.py @@ -0,0 +1,28 @@ +# Copyright: (c) 2019, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible import constants as C +from ansible.errors import AnsibleUndefinedVariable + +# need to mock DEFAULT_JINJA2_NATIVE here so native modules are imported +# correctly within the template module +C.DEFAULT_JINJA2_NATIVE = True +from ansible.template import Templar + +from units.mock.loader import DictDataLoader + + +# https://github.com/ansible/ansible/issues/52158 +def test_undefined_variable(): + fake_loader = DictDataLoader({}) + variables = {} + templar = Templar(loader=fake_loader, variables=variables) + + with pytest.raises(AnsibleUndefinedVariable): + templar.template("{{ missing }}")