Allow for lazy evaluation of Jinja2 expressions (#56116)
This commit is contained in:
@ -0,0 +1,2 @@
|
||||
breaking_changes:
|
||||
- Allow for lazy evaluation of Jinja2 expressions (https://github.com/ansible/ansible/issues/56017)
|
||||
@ -19,7 +19,19 @@ This document is part of a collection on porting. The complete list of porting g
|
||||
Playbook
|
||||
========
|
||||
|
||||
No notable changes
|
||||
* Variables are now evaluated lazily; only when they are actually used. For example, in ansible-core 2.14 an expression ``{{ defined_variable or undefined_variable }}`` does not fail on ``undefined_variable`` if the first part of ``or`` is evaluated to ``True`` as it is not needed to evaluate the second part. One particular case of a change in behavior to note is the task below which uses the ``undefined`` test. Prior to version 2.14 this would result in a fatal error trying to access the undefined value in the dictionary. In 2.14 the assertion passes as the dictionary is evaluated as undefined through one of its undefined values:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- some_defined_dict_with_undefined_values is undefined
|
||||
vars:
|
||||
dict_value: 1
|
||||
some_defined_dict_with_undefined_values:
|
||||
key1: value1
|
||||
key2: '{{ dict_value }}'
|
||||
key3: '{{ undefined_dict_value }}'
|
||||
|
||||
|
||||
Command Line
|
||||
|
||||
@ -97,7 +97,15 @@ class AnsibleJ2Vars(Mapping):
|
||||
try:
|
||||
value = self._templar.template(variable)
|
||||
except AnsibleUndefinedVariable as e:
|
||||
raise AnsibleUndefinedVariable("%s: %s" % (to_native(variable), e.message))
|
||||
# Instead of failing here prematurely, return an Undefined
|
||||
# object which fails only after its first usage allowing us to
|
||||
# do lazy evaluation and passing it into filters/tests that
|
||||
# operate on such objects.
|
||||
return self._templar.environment.undefined(
|
||||
hint=f"{variable}: {e.message}",
|
||||
name=varname,
|
||||
exc=AnsibleUndefinedVariable,
|
||||
)
|
||||
except Exception as e:
|
||||
msg = getattr(e, 'message', None) or to_native(e)
|
||||
raise AnsibleError("An unhandled exception occurred while templating '%s'. "
|
||||
|
||||
24
test/integration/targets/template/lazy_eval.yml
Normal file
24
test/integration/targets/template/lazy_eval.yml
Normal file
@ -0,0 +1,24 @@
|
||||
- hosts: testhost
|
||||
gather_facts: false
|
||||
vars:
|
||||
deep_undefined: "{{ nested_undefined_variable }}"
|
||||
tasks:
|
||||
- name: These do not throw an error, deep_undefined is just evaluated to undefined, since 2.13
|
||||
assert:
|
||||
that:
|
||||
- lazy_eval or deep_undefined
|
||||
- deep_undefined is undefined
|
||||
- deep_undefined|default('defaulted') == 'defaulted'
|
||||
vars:
|
||||
lazy_eval: true
|
||||
|
||||
- name: EXPECTED FAILURE actually using deep_undefined fails
|
||||
debug:
|
||||
msg: "{{ deep_undefined }}"
|
||||
ignore_errors: true
|
||||
register: res
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- res.failed
|
||||
- res.msg is contains("'nested_undefined_variable' is undefined")
|
||||
@ -40,3 +40,5 @@ ansible-playbook unsafe.yml -v "$@"
|
||||
|
||||
# ensure Jinja2 overrides from a template are used
|
||||
ansible-playbook in_template_overrides.yml -v "$@"
|
||||
|
||||
ansible-playbook lazy_eval.yml -i ../../inventory -v "$@"
|
||||
|
||||
@ -121,19 +121,6 @@ class TestConditional(unittest.TestCase):
|
||||
self._eval_con,
|
||||
when, variables)
|
||||
|
||||
def test_dict_undefined_values_is_defined(self):
|
||||
variables = {'dict_value': 1,
|
||||
'some_defined_dict_with_undefined_values': {'key1': 'value1',
|
||||
'key2': '{{ dict_value }}',
|
||||
'key3': '{{ undefined_dict_value }}'
|
||||
}}
|
||||
|
||||
when = [u"some_defined_dict_with_undefined_values is defined"]
|
||||
self.assertRaisesRegex(errors.AnsibleError,
|
||||
"The conditional check 'some_defined_dict_with_undefined_values is defined' failed.",
|
||||
self._eval_con,
|
||||
when, variables)
|
||||
|
||||
def test_is_defined(self):
|
||||
variables = {'some_defined_thing': True}
|
||||
when = [u"some_defined_thing is defined"]
|
||||
|
||||
Reference in New Issue
Block a user