We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
when condition checks complain about templated variables
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
when templating checking

##### ANSIBLE VERSION
```
ansible 2.4.0 (devel 2af5556901) last updated 2017/05/26 11:55:34 (GMT +1000)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/will/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/will/src/ansible/lib/ansible
  executable location = /home/will/src/ansible/bin/ansible
  python version = 2.7.13 (default, Jan 12 2017, 17:59:37) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]
```

##### CONFIGURATION


##### OS / ENVIRONMENT
N/A

##### SUMMARY
ansible-playbook is warning about `when` conditions that are variables that are composed of other variables (e.g. anything of the form `x: "{{ y }}"` and then doing `when: x`

##### STEPS TO REPRODUCE
ansible-playbook playbook.yml

```yaml
$ cat playbook.yml 
- hosts: localhost
  connection: local
  gather_facts: False

  vars:
    x: hello
    y: world
    a_list:
      - "{{ x }}"
      - "{{ y }}"

  tasks:
    - debug:
        var: a_list
      when: a_list
```

##### EXPECTED RESULTS
As below without the WARNING

##### ACTUAL RESULTS
```

$ ansible-playbook playbook.yml -v
Using /etc/ansible/ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] *********************************************************************************************************************************************

TASK [debug] *************************************************************************************************************************************************
 [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: [u'{{ x }}', u'{{ y }}']

ok: [localhost] => {
    "a_list": [
        "hello", 
        "world"
    ]
}

PLAY RECAP ***************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0   

```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/playbook/conditional.py`
Content:
```
1 # (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
2 #
3 # This file is part of Ansible
4 #
5 # Ansible is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Ansible is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17 
18 # Make coding more python3-ish
19 from __future__ import (absolute_import, division, print_function)
20 __metaclass__ = type
21 
22 import ast
23 import re
24 
25 from jinja2.compiler import generate
26 from jinja2.exceptions import UndefinedError
27 
28 from ansible.errors import AnsibleError, AnsibleUndefinedVariable
29 from ansible.module_utils.six import text_type
30 from ansible.module_utils._text import to_native
31 from ansible.playbook.attribute import FieldAttribute
32 
33 try:
34     from __main__ import display
35 except ImportError:
36     from ansible.utils.display import Display
37     display = Display()
38 
39 
40 DEFINED_REGEX = re.compile(r'(hostvars\[.+\]|[\w_]+)\s+(not\s+is|is|is\s+not)\s+(defined|undefined)')
41 LOOKUP_REGEX = re.compile(r'lookup\s*\(')
42 VALID_VAR_REGEX = re.compile("^[_A-Za-z][_a-zA-Z0-9]*$")
43 
44 class Conditional:
45 
46     '''
47     This is a mix-in class, to be used with Base to allow the object
48     to be run conditionally when a condition is met or skipped.
49     '''
50 
51     _when = FieldAttribute(isa='list', default=[])
52 
53     def __init__(self, loader=None):
54         # when used directly, this class needs a loader, but we want to
55         # make sure we don't trample on the existing one if this class
56         # is used as a mix-in with a playbook base class
57         if not hasattr(self, '_loader'):
58             if loader is None:
59                 raise AnsibleError("a loader must be specified when using Conditional() directly")
60             else:
61                 self._loader = loader
62         super(Conditional, self).__init__()
63 
64     def _validate_when(self, attr, name, value):
65         if not isinstance(value, list):
66             setattr(self, name, [ value ])
67 
68     def _get_attr_when(self):
69         '''
70         Override for the 'tags' getattr fetcher, used from Base.
71         '''
72         when = self._attributes['when']
73         if when is None:
74             when = []
75         if hasattr(self, '_get_parent_attribute'):
76             when = self._get_parent_attribute('when', extend=True, prepend=True)
77         return when
78 
79     def extract_defined_undefined(self, conditional):
80         results = []
81 
82         cond = conditional
83         m = DEFINED_REGEX.search(cond)
84         while m:
85             results.append(m.groups())
86             cond = cond[m.end():]
87             m = DEFINED_REGEX.search(cond)
88 
89         return results
90 
91     def evaluate_conditional(self, templar, all_vars):
92         '''
93         Loops through the conditionals set on this object, returning
94         False if any of them evaluate as such.
95         '''
96 
97         # since this is a mix-in, it may not have an underlying datastructure
98         # associated with it, so we pull it out now in case we need it for
99         # error reporting below
100         ds = None
101         if hasattr(self, '_ds'):
102             ds = getattr(self, '_ds')
103 
104         try:
105             # this allows for direct boolean assignments to conditionals "when: False"
106             if isinstance(self.when, bool):
107                 return self.when
108 
109             for conditional in self.when:
110                 if not self._check_conditional(conditional, templar, all_vars):
111                     return False
112         except Exception as e:
113             raise AnsibleError(
114                 "The conditional check '%s' failed. The error was: %s" % (to_native(conditional), to_native(e)), obj=ds
115             )
116 
117         return True
118 
119     def _check_conditional(self, conditional, templar, all_vars):
120         '''
121         This method does the low-level evaluation of each conditional
122         set on this object, using jinja2 to wrap the conditionals for
123         evaluation.
124         '''
125 
126         original = conditional
127         if conditional is None or conditional == '':
128             return True
129 
130         # pull the "bare" var out, which allows for nested conditionals
131         # and things like:
132         # - assert:
133         #     that:
134         #     - item
135         #   with_items:
136         #   - 1 == 1
137         if conditional in all_vars and VALID_VAR_REGEX.match(conditional):
138             conditional = all_vars[conditional]
139 
140         if templar.is_template(conditional):
141             display.warning('when statements should not include jinja2 '
142                             'templating delimiters such as {{ }} or {%% %%}. '
143                             'Found: %s' % conditional)
144 
145         # make sure the templar is using the variables specified with this method
146         templar.set_available_variables(variables=all_vars)
147 
148         try:
149             # if the conditional is "unsafe", disable lookups
150             disable_lookups = hasattr(conditional, '__UNSAFE__')
151             conditional = templar.template(conditional, disable_lookups=disable_lookups)
152             if not isinstance(conditional, text_type) or conditional == "":
153                 return conditional
154 
155             # update the lookups flag, as the string returned above may now be unsafe
156             # and we don't want future templating calls to do unsafe things
157             disable_lookups |= hasattr(conditional, '__UNSAFE__')
158 
159             # First, we do some low-level jinja2 parsing involving the AST format of the
160             # statement to ensure we don't do anything unsafe (using the disable_lookup flag above)
161             class CleansingNodeVisitor(ast.NodeVisitor):
162                 def generic_visit(self, node, inside_call=False, inside_yield=False):
163                     if isinstance(node, ast.Call):
164                         inside_call = True
165                     elif isinstance(node, ast.Yield):
166                         inside_yield = True
167                     elif isinstance(node, ast.Str):
168                         if disable_lookups:
169                             if inside_call and node.s.startswith("__"):
170                                 # calling things with a dunder is generally bad at this point...
171                                 raise AnsibleError(
172                                     "Invalid access found in the conditional: '%s'" % conditional
173                                 )
174                             elif inside_yield:
175                                 # we're inside a yield, so recursively parse and traverse the AST
176                                 # of the result to catch forbidden syntax from executing
177                                 parsed = ast.parse(node.s, mode='exec')
178                                 cnv = CleansingNodeVisitor()
179                                 cnv.visit(parsed)
180                     # iterate over all child nodes
181                     for child_node in ast.iter_child_nodes(node):
182                         self.generic_visit(
183                             child_node,
184                             inside_call=inside_call,
185                             inside_yield=inside_yield
186                         )
187             try:
188                 e = templar.environment.overlay()
189                 e.filters.update(templar._get_filters())
190                 e.tests.update(templar._get_tests())
191 
192                 res = e._parse(conditional, None, None)
193                 res = generate(res, e, None, None)
194                 parsed = ast.parse(res, mode='exec')
195 
196                 cnv = CleansingNodeVisitor()
197                 cnv.visit(parsed)
198             except Exception as e:
199                 raise AnsibleError("Invalid conditional detected: %s" % to_native(e))
200 
201             # and finally we generate and template the presented string and look at the resulting string
202             presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional
203             val = templar.template(presented, disable_lookups=disable_lookups).strip()
204             if val == "True":
205                 return True
206             elif val == "False":
207                 return False
208             else:
209                 raise AnsibleError("unable to evaluate conditional: %s" % original)
210         except (AnsibleUndefinedVariable, UndefinedError) as e:
211             # the templating failed, meaning most likely a variable was undefined. If we happened
212             # to be looking for an undefined variable, return True, otherwise fail
213             try:
214                 # first we extract the variable name from the error message
215                 var_name = re.compile(r"'(hostvars\[.+\]|[\w_]+)' is undefined").search(str(e)).groups()[0]
216                 # next we extract all defined/undefined tests from the conditional string
217                 def_undef = self.extract_defined_undefined(conditional)
218                 # then we loop through these, comparing the error variable name against
219                 # each def/undef test we found above. If there is a match, we determine
220                 # whether the logic/state mean the variable should exist or not and return
221                 # the corresponding True/False
222                 for (du_var, logic, state) in def_undef:
223                     # when we compare the var names, normalize quotes because something
224                     # like hostvars['foo'] may be tested against hostvars["foo"]
225                     if var_name.replace("'", '"') == du_var.replace("'", '"'):
226                         # the should exist is a xor test between a negation in the logic portion
227                         # against the state (defined or undefined)
228                         should_exist = ('not' in logic) != (state == 'defined')
229                         if should_exist:
230                             return False
231                         else:
232                             return True
233                 # as nothing above matched the failed var name, re-raise here to
234                 # trigger the AnsibleUndefinedVariable exception again below
235                 raise
236             except Exception as new_e:
237                 raise AnsibleUndefinedVariable(
238                     "error while evaluating conditional (%s): %s" % (original, e)
239                 )
240 
241
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/playbook/conditional.py b/lib/ansible/playbook/conditional.py
--- a/lib/ansible/playbook/conditional.py
+++ b/lib/ansible/playbook/conditional.py
@@ -127,6 +127,11 @@
         if conditional is None or conditional == '':
             return True
 
+        if templar.is_template(conditional):
+            display.warning('when statements should not include jinja2 '
+                            'templating delimiters such as {{ }} or {%% %%}. '
+                            'Found: %s' % conditional)
+
         # pull the "bare" var out, which allows for nested conditionals
         # and things like:
         # - assert:
@@ -137,11 +142,6 @@
         if conditional in all_vars and VALID_VAR_REGEX.match(conditional):
             conditional = all_vars[conditional]
 
-        if templar.is_template(conditional):
-            display.warning('when statements should not include jinja2 '
-                            'templating delimiters such as {{ }} or {%% %%}. '
-                            'Found: %s' % conditional)
-
         # make sure the templar is using the variables specified with this method
         templar.set_available_variables(variables=all_vars)

{"golden_diff": "diff --git a/lib/ansible/playbook/conditional.py b/lib/ansible/playbook/conditional.py\n--- a/lib/ansible/playbook/conditional.py\n+++ b/lib/ansible/playbook/conditional.py\n@@ -127,6 +127,11 @@\n         if conditional is None or conditional == '':\n             return True\n \n+        if templar.is_template(conditional):\n+            display.warning('when statements should not include jinja2 '\n+                            'templating delimiters such as {{ }} or {%% %%}. '\n+                            'Found: %s' % conditional)\n+\n         # pull the \"bare\" var out, which allows for nested conditionals\n         # and things like:\n         # - assert:\n@@ -137,11 +142,6 @@\n         if conditional in all_vars and VALID_VAR_REGEX.match(conditional):\n             conditional = all_vars[conditional]\n \n-        if templar.is_template(conditional):\n-            display.warning('when statements should not include jinja2 '\n-                            'templating delimiters such as {{ }} or {%% %%}. '\n-                            'Found: %s' % conditional)\n-\n         # make sure the templar is using the variables specified with this method\n         templar.set_available_variables(variables=all_vars)\n", "issue": "when condition checks complain about templated variables\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### COMPONENT NAME\r\nwhen templating checking\r\n\r\n##### ANSIBLE VERSION\r\n```\r\nansible 2.4.0 (devel 2af5556901) last updated 2017/05/26 11:55:34 (GMT +1000)\r\n  config file = /etc/ansible/ansible.cfg\r\n  configured module search path = [u'/home/will/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /home/will/src/ansible/lib/ansible\r\n  executable location = /home/will/src/ansible/bin/ansible\r\n  python version = 2.7.13 (default, Jan 12 2017, 17:59:37) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]\r\n```\r\n\r\n##### CONFIGURATION\r\n\r\n\r\n##### OS / ENVIRONMENT\r\nN/A\r\n\r\n##### SUMMARY\r\nansible-playbook is warning about `when` conditions that are variables that are composed of other variables (e.g. anything of the form `x: \"{{ y }}\"` and then doing `when: x`\r\n\r\n##### STEPS TO REPRODUCE\r\nansible-playbook playbook.yml\r\n\r\n```yaml\r\n$ cat playbook.yml \r\n- hosts: localhost\r\n  connection: local\r\n  gather_facts: False\r\n\r\n  vars:\r\n    x: hello\r\n    y: world\r\n    a_list:\r\n      - \"{{ x }}\"\r\n      - \"{{ y }}\"\r\n\r\n  tasks:\r\n    - debug:\r\n        var: a_list\r\n      when: a_list\r\n```\r\n\r\n##### EXPECTED RESULTS\r\nAs below without the WARNING\r\n\r\n##### ACTUAL RESULTS\r\n```\r\n\r\n$ ansible-playbook playbook.yml -v\r\nUsing /etc/ansible/ansible.cfg as config file\r\n [WARNING]: provided hosts list is empty, only localhost is available\r\n\r\n\r\nPLAY [localhost] *********************************************************************************************************************************************\r\n\r\nTASK [debug] *************************************************************************************************************************************************\r\n [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: [u'{{ x }}', u'{{ y }}']\r\n\r\nok: [localhost] => {\r\n    \"a_list\": [\r\n        \"hello\", \r\n        \"world\"\r\n    ]\r\n}\r\n\r\nPLAY RECAP ***************************************************************************************************************************************************\r\nlocalhost                  : ok=1    changed=0    unreachable=0    failed=0   \r\n\r\n```\r\n\n", "before_files": [{"content": "# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\n# Make coding more python3-ish\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport ast\nimport re\n\nfrom jinja2.compiler import generate\nfrom jinja2.exceptions import UndefinedError\n\nfrom ansible.errors import AnsibleError, AnsibleUndefinedVariable\nfrom ansible.module_utils.six import text_type\nfrom ansible.module_utils._text import to_native\nfrom ansible.playbook.attribute import FieldAttribute\n\ntry:\n    from __main__ import display\nexcept ImportError:\n    from ansible.utils.display import Display\n    display = Display()\n\n\nDEFINED_REGEX = re.compile(r'(hostvars\\[.+\\]|[\\w_]+)\\s+(not\\s+is|is|is\\s+not)\\s+(defined|undefined)')\nLOOKUP_REGEX = re.compile(r'lookup\\s*\\(')\nVALID_VAR_REGEX = re.compile(\"^[_A-Za-z][_a-zA-Z0-9]*$\")\n\nclass Conditional:\n\n    '''\n    This is a mix-in class, to be used with Base to allow the object\n    to be run conditionally when a condition is met or skipped.\n    '''\n\n    _when = FieldAttribute(isa='list', default=[])\n\n    def __init__(self, loader=None):\n        # when used directly, this class needs a loader, but we want to\n        # make sure we don't trample on the existing one if this class\n        # is used as a mix-in with a playbook base class\n        if not hasattr(self, '_loader'):\n            if loader is None:\n                raise AnsibleError(\"a loader must be specified when using Conditional() directly\")\n            else:\n                self._loader = loader\n        super(Conditional, self).__init__()\n\n    def _validate_when(self, attr, name, value):\n        if not isinstance(value, list):\n            setattr(self, name, [ value ])\n\n    def _get_attr_when(self):\n        '''\n        Override for the 'tags' getattr fetcher, used from Base.\n        '''\n        when = self._attributes['when']\n        if when is None:\n            when = []\n        if hasattr(self, '_get_parent_attribute'):\n            when = self._get_parent_attribute('when', extend=True, prepend=True)\n        return when\n\n    def extract_defined_undefined(self, conditional):\n        results = []\n\n        cond = conditional\n        m = DEFINED_REGEX.search(cond)\n        while m:\n            results.append(m.groups())\n            cond = cond[m.end():]\n            m = DEFINED_REGEX.search(cond)\n\n        return results\n\n    def evaluate_conditional(self, templar, all_vars):\n        '''\n        Loops through the conditionals set on this object, returning\n        False if any of them evaluate as such.\n        '''\n\n        # since this is a mix-in, it may not have an underlying datastructure\n        # associated with it, so we pull it out now in case we need it for\n        # error reporting below\n        ds = None\n        if hasattr(self, '_ds'):\n            ds = getattr(self, '_ds')\n\n        try:\n            # this allows for direct boolean assignments to conditionals \"when: False\"\n            if isinstance(self.when, bool):\n                return self.when\n\n            for conditional in self.when:\n                if not self._check_conditional(conditional, templar, all_vars):\n                    return False\n        except Exception as e:\n            raise AnsibleError(\n                \"The conditional check '%s' failed. The error was: %s\" % (to_native(conditional), to_native(e)), obj=ds\n            )\n\n        return True\n\n    def _check_conditional(self, conditional, templar, all_vars):\n        '''\n        This method does the low-level evaluation of each conditional\n        set on this object, using jinja2 to wrap the conditionals for\n        evaluation.\n        '''\n\n        original = conditional\n        if conditional is None or conditional == '':\n            return True\n\n        # pull the \"bare\" var out, which allows for nested conditionals\n        # and things like:\n        # - assert:\n        #     that:\n        #     - item\n        #   with_items:\n        #   - 1 == 1\n        if conditional in all_vars and VALID_VAR_REGEX.match(conditional):\n            conditional = all_vars[conditional]\n\n        if templar.is_template(conditional):\n            display.warning('when statements should not include jinja2 '\n                            'templating delimiters such as {{ }} or {%% %%}. '\n                            'Found: %s' % conditional)\n\n        # make sure the templar is using the variables specified with this method\n        templar.set_available_variables(variables=all_vars)\n\n        try:\n            # if the conditional is \"unsafe\", disable lookups\n            disable_lookups = hasattr(conditional, '__UNSAFE__')\n            conditional = templar.template(conditional, disable_lookups=disable_lookups)\n            if not isinstance(conditional, text_type) or conditional == \"\":\n                return conditional\n\n            # update the lookups flag, as the string returned above may now be unsafe\n            # and we don't want future templating calls to do unsafe things\n            disable_lookups |= hasattr(conditional, '__UNSAFE__')\n\n            # First, we do some low-level jinja2 parsing involving the AST format of the\n            # statement to ensure we don't do anything unsafe (using the disable_lookup flag above)\n            class CleansingNodeVisitor(ast.NodeVisitor):\n                def generic_visit(self, node, inside_call=False, inside_yield=False):\n                    if isinstance(node, ast.Call):\n                        inside_call = True\n                    elif isinstance(node, ast.Yield):\n                        inside_yield = True\n                    elif isinstance(node, ast.Str):\n                        if disable_lookups:\n                            if inside_call and node.s.startswith(\"__\"):\n                                # calling things with a dunder is generally bad at this point...\n                                raise AnsibleError(\n                                    \"Invalid access found in the conditional: '%s'\" % conditional\n                                )\n                            elif inside_yield:\n                                # we're inside a yield, so recursively parse and traverse the AST\n                                # of the result to catch forbidden syntax from executing\n                                parsed = ast.parse(node.s, mode='exec')\n                                cnv = CleansingNodeVisitor()\n                                cnv.visit(parsed)\n                    # iterate over all child nodes\n                    for child_node in ast.iter_child_nodes(node):\n                        self.generic_visit(\n                            child_node,\n                            inside_call=inside_call,\n                            inside_yield=inside_yield\n                        )\n            try:\n                e = templar.environment.overlay()\n                e.filters.update(templar._get_filters())\n                e.tests.update(templar._get_tests())\n\n                res = e._parse(conditional, None, None)\n                res = generate(res, e, None, None)\n                parsed = ast.parse(res, mode='exec')\n\n                cnv = CleansingNodeVisitor()\n                cnv.visit(parsed)\n            except Exception as e:\n                raise AnsibleError(\"Invalid conditional detected: %s\" % to_native(e))\n\n            # and finally we generate and template the presented string and look at the resulting string\n            presented = \"{%% if %s %%} True {%% else %%} False {%% endif %%}\" % conditional\n            val = templar.template(presented, disable_lookups=disable_lookups).strip()\n            if val == \"True\":\n                return True\n            elif val == \"False\":\n                return False\n            else:\n                raise AnsibleError(\"unable to evaluate conditional: %s\" % original)\n        except (AnsibleUndefinedVariable, UndefinedError) as e:\n            # the templating failed, meaning most likely a variable was undefined. If we happened\n            # to be looking for an undefined variable, return True, otherwise fail\n            try:\n                # first we extract the variable name from the error message\n                var_name = re.compile(r\"'(hostvars\\[.+\\]|[\\w_]+)' is undefined\").search(str(e)).groups()[0]\n                # next we extract all defined/undefined tests from the conditional string\n                def_undef = self.extract_defined_undefined(conditional)\n                # then we loop through these, comparing the error variable name against\n                # each def/undef test we found above. If there is a match, we determine\n                # whether the logic/state mean the variable should exist or not and return\n                # the corresponding True/False\n                for (du_var, logic, state) in def_undef:\n                    # when we compare the var names, normalize quotes because something\n                    # like hostvars['foo'] may be tested against hostvars[\"foo\"]\n                    if var_name.replace(\"'\", '\"') == du_var.replace(\"'\", '\"'):\n                        # the should exist is a xor test between a negation in the logic portion\n                        # against the state (defined or undefined)\n                        should_exist = ('not' in logic) != (state == 'defined')\n                        if should_exist:\n                            return False\n                        else:\n                            return True\n                # as nothing above matched the failed var name, re-raise here to\n                # trigger the AnsibleUndefinedVariable exception again below\n                raise\n            except Exception as new_e:\n                raise AnsibleUndefinedVariable(\n                    \"error while evaluating conditional (%s): %s\" % (original, e)\n                )\n\n", "path": "lib/ansible/playbook/conditional.py"}], "after_files": [{"content": "# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\n# Make coding more python3-ish\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport ast\nimport re\n\nfrom jinja2.compiler import generate\nfrom jinja2.exceptions import UndefinedError\n\nfrom ansible.errors import AnsibleError, AnsibleUndefinedVariable\nfrom ansible.module_utils.six import text_type\nfrom ansible.module_utils._text import to_native\nfrom ansible.playbook.attribute import FieldAttribute\n\ntry:\n    from __main__ import display\nexcept ImportError:\n    from ansible.utils.display import Display\n    display = Display()\n\n\nDEFINED_REGEX = re.compile(r'(hostvars\\[.+\\]|[\\w_]+)\\s+(not\\s+is|is|is\\s+not)\\s+(defined|undefined)')\nLOOKUP_REGEX = re.compile(r'lookup\\s*\\(')\nVALID_VAR_REGEX = re.compile(\"^[_A-Za-z][_a-zA-Z0-9]*$\")\n\nclass Conditional:\n\n    '''\n    This is a mix-in class, to be used with Base to allow the object\n    to be run conditionally when a condition is met or skipped.\n    '''\n\n    _when = FieldAttribute(isa='list', default=[])\n\n    def __init__(self, loader=None):\n        # when used directly, this class needs a loader, but we want to\n        # make sure we don't trample on the existing one if this class\n        # is used as a mix-in with a playbook base class\n        if not hasattr(self, '_loader'):\n            if loader is None:\n                raise AnsibleError(\"a loader must be specified when using Conditional() directly\")\n            else:\n                self._loader = loader\n        super(Conditional, self).__init__()\n\n    def _validate_when(self, attr, name, value):\n        if not isinstance(value, list):\n            setattr(self, name, [ value ])\n\n    def _get_attr_when(self):\n        '''\n        Override for the 'tags' getattr fetcher, used from Base.\n        '''\n        when = self._attributes['when']\n        if when is None:\n            when = []\n        if hasattr(self, '_get_parent_attribute'):\n            when = self._get_parent_attribute('when', extend=True, prepend=True)\n        return when\n\n    def extract_defined_undefined(self, conditional):\n        results = []\n\n        cond = conditional\n        m = DEFINED_REGEX.search(cond)\n        while m:\n            results.append(m.groups())\n            cond = cond[m.end():]\n            m = DEFINED_REGEX.search(cond)\n\n        return results\n\n    def evaluate_conditional(self, templar, all_vars):\n        '''\n        Loops through the conditionals set on this object, returning\n        False if any of them evaluate as such.\n        '''\n\n        # since this is a mix-in, it may not have an underlying datastructure\n        # associated with it, so we pull it out now in case we need it for\n        # error reporting below\n        ds = None\n        if hasattr(self, '_ds'):\n            ds = getattr(self, '_ds')\n\n        try:\n            # this allows for direct boolean assignments to conditionals \"when: False\"\n            if isinstance(self.when, bool):\n                return self.when\n\n            for conditional in self.when:\n                if not self._check_conditional(conditional, templar, all_vars):\n                    return False\n        except Exception as e:\n            raise AnsibleError(\n                \"The conditional check '%s' failed. The error was: %s\" % (to_native(conditional), to_native(e)), obj=ds\n            )\n\n        return True\n\n    def _check_conditional(self, conditional, templar, all_vars):\n        '''\n        This method does the low-level evaluation of each conditional\n        set on this object, using jinja2 to wrap the conditionals for\n        evaluation.\n        '''\n\n        original = conditional\n        if conditional is None or conditional == '':\n            return True\n\n        if templar.is_template(conditional):\n            display.warning('when statements should not include jinja2 '\n                            'templating delimiters such as {{ }} or {%% %%}. '\n                            'Found: %s' % conditional)\n\n        # pull the \"bare\" var out, which allows for nested conditionals\n        # and things like:\n        # - assert:\n        #     that:\n        #     - item\n        #   with_items:\n        #   - 1 == 1\n        if conditional in all_vars and VALID_VAR_REGEX.match(conditional):\n            conditional = all_vars[conditional]\n\n        # make sure the templar is using the variables specified with this method\n        templar.set_available_variables(variables=all_vars)\n\n        try:\n            # if the conditional is \"unsafe\", disable lookups\n            disable_lookups = hasattr(conditional, '__UNSAFE__')\n            conditional = templar.template(conditional, disable_lookups=disable_lookups)\n            if not isinstance(conditional, text_type) or conditional == \"\":\n                return conditional\n\n            # update the lookups flag, as the string returned above may now be unsafe\n            # and we don't want future templating calls to do unsafe things\n            disable_lookups |= hasattr(conditional, '__UNSAFE__')\n\n            # First, we do some low-level jinja2 parsing involving the AST format of the\n            # statement to ensure we don't do anything unsafe (using the disable_lookup flag above)\n            class CleansingNodeVisitor(ast.NodeVisitor):\n                def generic_visit(self, node, inside_call=False, inside_yield=False):\n                    if isinstance(node, ast.Call):\n                        inside_call = True\n                    elif isinstance(node, ast.Yield):\n                        inside_yield = True\n                    elif isinstance(node, ast.Str):\n                        if disable_lookups:\n                            if inside_call and node.s.startswith(\"__\"):\n                                # calling things with a dunder is generally bad at this point...\n                                raise AnsibleError(\n                                    \"Invalid access found in the conditional: '%s'\" % conditional\n                                )\n                            elif inside_yield:\n                                # we're inside a yield, so recursively parse and traverse the AST\n                                # of the result to catch forbidden syntax from executing\n                                parsed = ast.parse(node.s, mode='exec')\n                                cnv = CleansingNodeVisitor()\n                                cnv.visit(parsed)\n                    # iterate over all child nodes\n                    for child_node in ast.iter_child_nodes(node):\n                        self.generic_visit(\n                            child_node,\n                            inside_call=inside_call,\n                            inside_yield=inside_yield\n                        )\n            try:\n                e = templar.environment.overlay()\n                e.filters.update(templar._get_filters())\n                e.tests.update(templar._get_tests())\n\n                res = e._parse(conditional, None, None)\n                res = generate(res, e, None, None)\n                parsed = ast.parse(res, mode='exec')\n\n                cnv = CleansingNodeVisitor()\n                cnv.visit(parsed)\n            except Exception as e:\n                raise AnsibleError(\"Invalid conditional detected: %s\" % to_native(e))\n\n            # and finally we generate and template the presented string and look at the resulting string\n            presented = \"{%% if %s %%} True {%% else %%} False {%% endif %%}\" % conditional\n            val = templar.template(presented, disable_lookups=disable_lookups).strip()\n            if val == \"True\":\n                return True\n            elif val == \"False\":\n                return False\n            else:\n                raise AnsibleError(\"unable to evaluate conditional: %s\" % original)\n        except (AnsibleUndefinedVariable, UndefinedError) as e:\n            # the templating failed, meaning most likely a variable was undefined. If we happened\n            # to be looking for an undefined variable, return True, otherwise fail\n            try:\n                # first we extract the variable name from the error message\n                var_name = re.compile(r\"'(hostvars\\[.+\\]|[\\w_]+)' is undefined\").search(str(e)).groups()[0]\n                # next we extract all defined/undefined tests from the conditional string\n                def_undef = self.extract_defined_undefined(conditional)\n                # then we loop through these, comparing the error variable name against\n                # each def/undef test we found above. If there is a match, we determine\n                # whether the logic/state mean the variable should exist or not and return\n                # the corresponding True/False\n                for (du_var, logic, state) in def_undef:\n                    # when we compare the var names, normalize quotes because something\n                    # like hostvars['foo'] may be tested against hostvars[\"foo\"]\n                    if var_name.replace(\"'\", '\"') == du_var.replace(\"'\", '\"'):\n                        # the should exist is a xor test between a negation in the logic portion\n                        # against the state (defined or undefined)\n                        should_exist = ('not' in logic) != (state == 'defined')\n                        if should_exist:\n                            return False\n                        else:\n                            return True\n                # as nothing above matched the failed var name, re-raise here to\n                # trigger the AnsibleUndefinedVariable exception again below\n                raise\n            except Exception as new_e:\n                raise AnsibleUndefinedVariable(\n                    \"error while evaluating conditional (%s): %s\" % (original, e)\n                )\n\n", "path": "lib/ansible/playbook/conditional.py"}]}