Native Jinja2: raise undefined error immediately (#52237)

Fixes #52158
This commit is contained in:
Martin Krizek 2019-02-15 19:36:12 +01:00 committed by GitHub
parent 02c9f70898
commit 50d7483632
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 0 deletions

View file

@ -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).

View file

@ -12,6 +12,7 @@ import types
from jinja2._compat import text_type from jinja2._compat import text_type
from jinja2.runtime import StrictUndefined
from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode
@ -37,6 +38,17 @@ def ansible_native_concat(nodes):
if isinstance(out, AnsibleVaultEncryptedUnicode): if isinstance(out, AnsibleVaultEncryptedUnicode):
return out.data 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 # short circuit literal_eval when possible
if not isinstance(out, list): if not isinstance(out, list):
return out return out

View file

@ -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 }}")