diff options
Diffstat (limited to 'lib/ansible/template/__init__.py')
-rw-r--r-- | lib/ansible/template/__init__.py | 109 |
1 files changed, 50 insertions, 59 deletions
diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py index 05aab631..c45cfe35 100644 --- a/lib/ansible/template/__init__.py +++ b/lib/ansible/template/__init__.py @@ -45,8 +45,8 @@ from ansible.errors import ( AnsibleOptionsError, AnsibleUndefinedVariable, ) -from ansible.module_utils.six import string_types -from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes +from ansible.module_utils.six import string_types, text_type +from ansible.module_utils._text import to_native, to_text, to_bytes from ansible.module_utils.common.collections import is_sequence from ansible.plugins.loader import filter_loader, lookup_loader, test_loader from ansible.template.native_helpers import ansible_native_concat, ansible_eval_concat, ansible_concat @@ -55,7 +55,7 @@ from ansible.template.vars import AnsibleJ2Vars from ansible.utils.display import Display from ansible.utils.listify import listify_lookup_plugin_terms from ansible.utils.native_jinja import NativeJinjaText -from ansible.utils.unsafe_proxy import to_unsafe_text, wrap_var, AnsibleUnsafeText, AnsibleUnsafeBytes, NativeJinjaUnsafeText +from ansible.utils.unsafe_proxy import wrap_var, AnsibleUnsafeText, AnsibleUnsafeBytes, NativeJinjaUnsafeText display = Display() @@ -103,9 +103,9 @@ def generate_ansible_template_vars(path, fullpath=None, dest_path=None): managed_str = managed_default.format( host=temp_vars['template_host'], uid=temp_vars['template_uid'], - file=temp_vars['template_path'].replace('%', '%%'), + file=temp_vars['template_path'], ) - temp_vars['ansible_managed'] = to_unsafe_text(time.strftime(to_native(managed_str), time.localtime(os.path.getmtime(b_path)))) + temp_vars['ansible_managed'] = to_text(time.strftime(to_native(managed_str), time.localtime(os.path.getmtime(b_path)))) return temp_vars @@ -130,7 +130,7 @@ def _escape_backslashes(data, jinja_env): backslashes inside of a jinja2 expression. """ - if '\\' in data and jinja_env.variable_start_string in data: + if '\\' in data and '{{' in data: new_data = [] d2 = jinja_env.preprocess(data) in_var = False @@ -153,39 +153,6 @@ def _escape_backslashes(data, jinja_env): return data -def _create_overlay(data, overrides, jinja_env): - if overrides is None: - overrides = {} - - try: - has_override_header = data.startswith(JINJA2_OVERRIDE) - except (TypeError, AttributeError): - has_override_header = False - - if overrides or has_override_header: - overlay = jinja_env.overlay(**overrides) - else: - overlay = jinja_env - - # Get jinja env overrides from template - if has_override_header: - eol = data.find('\n') - line = data[len(JINJA2_OVERRIDE):eol] - data = data[eol + 1:] - for pair in line.split(','): - if ':' not in pair: - raise AnsibleError("failed to parse jinja2 override '%s'." - " Did you use something different from colon as key-value separator?" % pair.strip()) - (key, val) = pair.split(':', 1) - key = key.strip() - if hasattr(overlay, key): - setattr(overlay, key, ast.literal_eval(val.strip())) - else: - display.warning(f"Could not find Jinja2 environment setting to override: '{key}'") - - return data, overlay - - def is_possibly_template(data, jinja_env): """Determines if a string looks like a template, by seeing if it contains a jinja2 start delimiter. Does not guarantee that the string @@ -565,7 +532,7 @@ class AnsibleEnvironment(NativeEnvironment): ''' context_class = AnsibleContext template_class = AnsibleJ2Template - concat = staticmethod(ansible_eval_concat) # type: ignore[assignment] + concat = staticmethod(ansible_eval_concat) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -580,7 +547,7 @@ class AnsibleEnvironment(NativeEnvironment): class AnsibleNativeEnvironment(AnsibleEnvironment): - concat = staticmethod(ansible_native_concat) # type: ignore[assignment] + concat = staticmethod(ansible_native_concat) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -592,7 +559,14 @@ class Templar: The main class for templating, with the main entry-point of template(). ''' - def __init__(self, loader, variables=None): + def __init__(self, loader, shared_loader_obj=None, variables=None): + if shared_loader_obj is not None: + display.deprecated( + "The `shared_loader_obj` option to `Templar` is no longer functional, " + "ansible.plugins.loader is used directly instead.", + version='2.16', + ) + self._loader = loader self._available_variables = {} if variables is None else variables @@ -606,6 +580,9 @@ class Templar: ) self.environment.template_class.environment_class = environment_class + # jinja2 global is inconsistent across versions, this normalizes them + self.environment.globals['dict'] = dict + # Custom globals self.environment.globals['lookup'] = self._lookup self.environment.globals['query'] = self.environment.globals['q'] = self._query_lookup @@ -615,14 +592,11 @@ class Templar: # the current rendering context under which the templar class is working self.cur_context = None - # this regex is re-compiled each time variable_start_string and variable_end_string are possibly changed - self._compile_single_var(self.environment) + # FIXME this regex should be re-compiled each time variable_start_string and variable_end_string are changed + self.SINGLE_VAR = re.compile(r"^%s\s*(\w*)\s*%s$" % (self.environment.variable_start_string, self.environment.variable_end_string)) self.jinja2_native = C.DEFAULT_JINJA2_NATIVE - def _compile_single_var(self, env): - self.SINGLE_VAR = re.compile(r"^%s\s*(\w*)\s*%s$" % (env.variable_start_string, env.variable_end_string)) - def copy_with_new_env(self, environment_class=AnsibleEnvironment, **kwargs): r"""Creates a new copy of Templar with a new environment. @@ -745,7 +719,7 @@ class Templar: variable = self._convert_bare_variable(variable) if isinstance(variable, string_types): - if not self.is_possibly_template(variable, overrides): + if not self.is_possibly_template(variable): return variable # Check to see if the string we are trying to render is just referencing a single @@ -770,7 +744,6 @@ class Templar: disable_lookups=disable_lookups, convert_data=convert_data, ) - self._compile_single_var(self.environment) return result @@ -817,9 +790,8 @@ class Templar: templatable = is_template - def is_possibly_template(self, data, overrides=None): - data, env = _create_overlay(data, overrides, self.environment) - return is_possibly_template(data, env) + def is_possibly_template(self, data): + return is_possibly_template(data, self.environment) def _convert_bare_variable(self, variable): ''' @@ -843,7 +815,7 @@ class Templar: def _now_datetime(self, utc=False, fmt=None): '''jinja2 global function to return current datetime, potentially formatted via strftime''' if utc: - now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) + now = datetime.datetime.utcnow() else: now = datetime.datetime.now() @@ -852,12 +824,12 @@ class Templar: return now - def _query_lookup(self, name, /, *args, **kwargs): + def _query_lookup(self, name, *args, **kwargs): ''' wrapper for lookup, force wantlist true''' kwargs['wantlist'] = True return self._lookup(name, *args, **kwargs) - def _lookup(self, name, /, *args, **kwargs): + def _lookup(self, name, *args, **kwargs): instance = lookup_loader.get(name, loader=self._loader, templar=self) if instance is None: @@ -960,12 +932,31 @@ class Templar: if fail_on_undefined is None: fail_on_undefined = self._fail_on_undefined_errors + has_template_overrides = data.startswith(JINJA2_OVERRIDE) + try: # NOTE Creating an overlay that lives only inside do_template means that overrides are not applied # when templating nested variables in AnsibleJ2Vars where Templar.environment is used, not the overlay. - data, myenv = _create_overlay(data, overrides, self.environment) - # in case delimiters change - self._compile_single_var(myenv) + # This is historic behavior that is kept for backwards compatibility. + if overrides: + myenv = self.environment.overlay(overrides) + elif has_template_overrides: + myenv = self.environment.overlay() + else: + myenv = self.environment + + # Get jinja env overrides from template + if has_template_overrides: + eol = data.find('\n') + line = data[len(JINJA2_OVERRIDE):eol] + data = data[eol + 1:] + for pair in line.split(','): + if ':' not in pair: + raise AnsibleError("failed to parse jinja2 override '%s'." + " Did you use something different from colon as key-value separator?" % pair.strip()) + (key, val) = pair.split(':', 1) + key = key.strip() + setattr(myenv, key, ast.literal_eval(val.strip())) if escape_backslashes: # Allow users to specify backslashes in playbooks as "\\" instead of as "\\\\". @@ -973,7 +964,7 @@ class Templar: try: t = myenv.from_string(data) - except (TemplateSyntaxError, SyntaxError) as e: + except TemplateSyntaxError as e: raise AnsibleError("template error while templating string: %s. String: %s" % (to_native(e), to_native(data)), orig_exc=e) except Exception as e: if 'recursion' in to_native(e): |