Prevent templating unused variables for {%include%} (#68749)

Fixes #68699
This commit is contained in:
Martin Krizek 2020-04-14 10:27:02 +02:00 committed by GitHub
parent 7d5177d6a0
commit ff1ba39c8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 3 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- "Prevent templating unused variables for {% include %} (https://github.com/ansible/ansible/issues/68699)"

View file

@ -28,6 +28,7 @@ import re
import time
from contextlib import contextmanager
from distutils.version import LooseVersion
from numbers import Number
try:
@ -67,6 +68,8 @@ NON_TEMPLATED_TYPES = (bool, Number)
JINJA2_OVERRIDE = '#jinja2:'
from jinja2 import __version__ as j2_version
USE_JINJA2_NATIVE = False
if C.DEFAULT_JINJA2_NATIVE:
try:
@ -76,7 +79,6 @@ if C.DEFAULT_JINJA2_NATIVE:
except ImportError:
from jinja2 import Environment
from jinja2.utils import concat as j2_concat
from jinja2 import __version__ as j2_version
display.warning(
'jinja2_native requires Jinja 2.10 and above. '
'Version detected: %s. Falling back to default.' % j2_version
@ -295,6 +297,40 @@ class AnsibleContext(Context):
self._update_unsafe(val)
return val
def get_all(self):
"""Return the complete context as a dict including the exported
variables. For optimizations reasons this might not return an
actual copy so be careful with using it.
This is to prevent from running ``AnsibleJ2Vars`` through dict():
``dict(self.parent, **self.vars)``
In Ansible this means that ALL variables would be templated in the
process of re-creating the parent because ``AnsibleJ2Vars`` templates
each variable in its ``__getitem__`` method. Instead we re-create the
parent via ``AnsibleJ2Vars.add_locals`` that creates a new
``AnsibleJ2Vars`` copy without templating each variable.
This will prevent unnecessarily templating unused variables in cases
like setting a local variable and passing it to {% include %}
in a template.
Also see ``AnsibleJ2Template``and
https://github.com/pallets/jinja/commit/d67f0fd4cc2a4af08f51f4466150d49da7798729
"""
if LooseVersion(j2_version) >= LooseVersion('2.9'):
if not self.vars:
return self.parent
if not self.parent:
return self.vars
if isinstance(self.parent, AnsibleJ2Vars):
return self.parent.add_locals(self.vars)
else:
# can this happen in Ansible?
return dict(self.parent, **self.vars)
class JinjaPluginIntercept(MutableMapping):
def __init__(self, delegatee, pluginloader, *args, **kwargs):

View file

@ -26,9 +26,9 @@ __all__ = ['AnsibleJ2Template']
class AnsibleJ2Template(jinja2.environment.Template):
'''
A helper class, which prevents Jinja2 from running _jinja2_vars through dict().
A helper class, which prevents Jinja2 from running AnsibleJ2Vars through dict().
Without this, {% include %} and similar will create new contexts unlike the special
one created in template_from_file. This ensures they are all alike, except for
one created in Templar.template. This ensures they are all alike, except for
potential locals.
'''

View file

@ -19,3 +19,6 @@ ansible-playbook corner_cases.yml -v "$@"
# Test for #57351
ansible-playbook filter_plugins.yml -v "$@"
# https://github.com/ansible/ansible/issues/68699
ansible-playbook unused_vars_include.yml -v "$@"

View file

@ -0,0 +1 @@
{{ var_set_in_template }}

View file

@ -0,0 +1,2 @@
{% set var_set_in_template=test_var %}
{% include "unused_vars_include.j2" %}

View file

@ -0,0 +1,8 @@
- hosts: localhost
gather_facts: no
vars:
test_var: foo
unused_var: "{{ undefined_var }}"
tasks:
- debug:
msg: "{{ lookup('template', 'unused_vars_template.j2') }}"