def test_safe_eval_usage(self): # test safe eval calls with different possible types for the # locals dictionary, to ensure we don't run into problems like # ansible/ansible/issues/12206 again for locals_vars in (dict(), defaultdict(dict)): self.assertEqual(safe_eval('True', locals=locals_vars), True) self.assertEqual(safe_eval('False', locals=locals_vars), False) self.assertEqual(safe_eval('0', locals=locals_vars), 0) self.assertEqual(safe_eval('[]', locals=locals_vars), []) self.assertEqual(safe_eval('{}', locals=locals_vars), {})
def listify_lookup_plugin_terms(terms, variables, loader): if isinstance(terms, basestring): # someone did: # with_items: alist # OR # with_items: {{ alist }} stripped = terms.strip() templar = Templar(loader=loader, variables=variables) if not (stripped.startswith('{') or stripped.startswith('[') ) and not stripped.startswith("/") and not stripped.startswith( 'set([') and not LOOKUP_REGEX.search(terms): # if not already a list, get ready to evaluate with Jinja2 # not sure why the "/" is in above code :) try: new_terms = templar.template("{{ %s }}" % terms) if isinstance(new_terms, basestring) and "{{" in new_terms: pass else: terms = new_terms except: pass else: terms = templar.template(terms) if '{' in terms or '[' in terms: # Jinja2 already evaluated a variable to a list. # Jinja2-ified list needs to be converted back to a real type return safe_eval(terms) if isinstance(terms, basestring): terms = [terms] return terms
def listify_lookup_plugin_terms(terms, variables, loader): if isinstance(terms, basestring): # someone did: # with_items: alist # OR # with_items: {{ alist }} stripped = terms.strip() templar = Templar(loader=loader, variables=variables) if not (stripped.startswith('{') or stripped.startswith('[')) and not stripped.startswith("/") and not stripped.startswith('set([') and not LOOKUP_REGEX.search(terms): # if not already a list, get ready to evaluate with Jinja2 # not sure why the "/" is in above code :) try: new_terms = templar.template("{{ %s }}" % terms) if isinstance(new_terms, basestring) and "{{" in new_terms: pass else: terms = new_terms except: pass else: terms = templar.template(terms) if '{' in terms or '[' in terms: # Jinja2 already evaluated a variable to a list. # Jinja2-ified list needs to be converted back to a real type return safe_eval(terms) if isinstance(terms, basestring): terms = [ terms ] return terms
def template(self, variable, convert_bare=False, preserve_trailing_newlines=False, fail_on_undefined=None, overrides=None, convert_data=True): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' try: if convert_bare: variable = self._convert_bare_variable(variable) if isinstance(variable, basestring): result = variable if self._contains_vars(variable): # Check to see if the string we are trying to render is just referencing a single # var. In this case we don't want to accidentally change the type of the variable # to a string by using the jinja template renderer. We just want to pass it. only_one = self.SINGLE_VAR.match(variable) if only_one: var_name = only_one.group(1) if var_name in self._available_variables: resolved_val = self._available_variables[var_name] if isinstance(resolved_val, NON_TEMPLATED_TYPES): return resolved_val elif resolved_val is None: return C.DEFAULT_NULL_REPRESENTATION result = self._do_template(variable, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) if convert_data: # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or \ result.startswith("[") or result in ("True", "False"): eval_results = safe_eval(result, locals=self._available_variables, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] else: # FIXME: if the safe_eval raised an error, should we do something with it? pass return result elif isinstance(variable, (list, tuple)): return [self.template(v, convert_bare=convert_bare, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) for v in variable] elif isinstance(variable, dict): d = {} for (k, v) in variable.iteritems(): d[k] = self.template(v, convert_bare=convert_bare, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable
def template(self, variable, convert_bare=False, preserve_trailing_newlines=False): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' try: if convert_bare: variable = self._convert_bare_variable(variable) if isinstance(variable, basestring): result = variable if self._contains_vars(variable): result = self._do_template( variable, preserve_trailing_newlines=preserve_trailing_newlines) # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith("{{") ) or result.startswith("["): eval_results = safe_eval( result, locals=self._available_variables, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] else: # FIXME: if the safe_eval raised an error, should we do something with it? pass return result elif isinstance(variable, (list, tuple)): return [ self.template(v, convert_bare=convert_bare) for v in variable ] elif isinstance(variable, dict): d = {} for (k, v) in variable.iteritems(): d[k] = self.template(v, convert_bare=convert_bare) return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable
def template(self, variable, convert_bare=False, preserve_trailing_newlines=False, fail_on_undefined=None, overrides=None): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' try: if convert_bare: variable = self._convert_bare_variable(variable) if isinstance(variable, basestring): result = variable if self._contains_vars(variable): # Check to see if the string we are trying to render is just referencing a single # var. In this case we don't want to accidentally change the type of the variable # to a string by using the jinja template renderer. We just want to pass it. only_one = self.SINGLE_VAR.match(variable) if only_one: var_name = only_one.group(1) if var_name in self._available_variables: resolved_val = self._available_variables[var_name] if isinstance(resolved_val, NON_TEMPLATED_TYPES): return resolved_val result = self._do_template(variable, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or result.startswith("[") or result in ("True", "False"): eval_results = safe_eval(result, locals=self._available_variables, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] else: # FIXME: if the safe_eval raised an error, should we do something with it? pass return result elif isinstance(variable, (list, tuple)): return [self.template(v, convert_bare=convert_bare, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) for v in variable] elif isinstance(variable, dict): d = {} for (k, v) in variable.iteritems(): d[k] = self.template(v, convert_bare=convert_bare, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable
def listify_lookup_plugin_terms(terms, variables, loader): if isinstance(terms, basestring): stripped = terms.strip() templar = Templar(loader=loader, variables=variables) # FIXME: warn/deprecation on bare vars in with_ so we can eventually remove fail on undefined override terms = templar.template(terms, convert_bare=True, fail_on_undefined=False) # TODO: check if this is needed as template should also return correct type already terms = safe_eval(terms) if isinstance(terms, basestring) or not isinstance(terms, Iterable): terms = [terms] return terms
def listify_lookup_plugin_terms(terms, variables, loader): if isinstance(terms, basestring): stripped = terms.strip() templar = Templar(loader=loader, variables=variables) #FIXME: warn/deprecation on bare vars in with_ so we can eventually remove fail on undefined override terms = templar.template(terms, convert_bare=True, fail_on_undefined=False) #TODO: check if this is needed as template should also return correct type already terms = safe_eval(terms) if isinstance(terms, basestring) or not isinstance(terms, Iterable): terms = [ terms ] return terms
def template(self, variable, convert_bare=False, preserve_trailing_newlines=False): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' try: if convert_bare: variable = self._convert_bare_variable(variable) if isinstance(variable, basestring): result = variable if self._contains_vars(variable): result = self._do_template(variable, preserve_trailing_newlines=preserve_trailing_newlines) # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith("{{")) or result.startswith("["): eval_results = safe_eval(result, locals=self._available_variables, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] else: # FIXME: if the safe_eval raised an error, should we do something with it? pass return result elif isinstance(variable, (list, tuple)): return [self.template(v, convert_bare=convert_bare) for v in variable] elif isinstance(variable, dict): d = {} for (k, v) in variable.iteritems(): d[k] = self.template(v, convert_bare=convert_bare) return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable
def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True, static_vars=None, cache=True, disable_lookups=False): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' static_vars = [''] if static_vars is None else static_vars # Don't template unsafe variables, just return them. if hasattr(variable, '__UNSAFE__'): return variable if fail_on_undefined is None: fail_on_undefined = self._fail_on_undefined_errors try: if convert_bare: variable = self._convert_bare_variable(variable) if isinstance(variable, string_types): result = variable if self.is_possibly_template(variable): # Check to see if the string we are trying to render is just referencing a single # var. In this case we don't want to accidentally change the type of the variable # to a string by using the jinja template renderer. We just want to pass it. only_one = self.SINGLE_VAR.match(variable) if only_one: var_name = only_one.group(1) if var_name in self._available_variables: resolved_val = self._available_variables[var_name] if isinstance(resolved_val, NON_TEMPLATED_TYPES): return resolved_val elif resolved_val is None: return C.DEFAULT_NULL_REPRESENTATION # Using a cache in order to prevent template calls with already templated variables sha1_hash = None if cache: variable_hash = sha1(text_type(variable).encode('utf-8')) options_hash = sha1( ( text_type(preserve_trailing_newlines) + text_type(escape_backslashes) + text_type(fail_on_undefined) + text_type(overrides) ).encode('utf-8') ) sha1_hash = variable_hash.hexdigest() + options_hash.hexdigest() if cache and sha1_hash in self._cached_result: result = self._cached_result[sha1_hash] else: result = self.do_template( variable, preserve_trailing_newlines=preserve_trailing_newlines, escape_backslashes=escape_backslashes, fail_on_undefined=fail_on_undefined, overrides=overrides, disable_lookups=disable_lookups, ) if not USE_JINJA2_NATIVE: unsafe = hasattr(result, '__UNSAFE__') if convert_data and not self._no_type_regex.match(variable): # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or \ result.startswith("[") or result in ("True", "False"): eval_results = safe_eval(result, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] if unsafe: result = wrap_var(result) else: # FIXME: if the safe_eval raised an error, should we do something with it? pass # we only cache in the case where we have a single variable # name, to make sure we're not putting things which may otherwise # be dynamic in the cache (filters, lookups, etc.) if cache and only_one: self._cached_result[sha1_hash] = result return result elif is_sequence(variable): return [self.template( v, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides, disable_lookups=disable_lookups, ) for v in variable] elif isinstance(variable, Mapping): d = {} # we don't use iteritems() here to avoid problems if the underlying dict # changes sizes due to the templating, which can happen with hostvars for k in variable.keys(): if k not in static_vars: d[k] = self.template( variable[k], preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides, disable_lookups=disable_lookups, ) else: d[k] = variable[k] return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable
def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True, static_vars = [''], cache = True, bare_deprecated=True): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' if fail_on_undefined is None: fail_on_undefined = self._fail_on_undefined_errors # Don't template unsafe variables, instead drop them back down to their constituent type. if hasattr(variable, '__UNSAFE__'): if isinstance(variable, text_type): return self._clean_data(variable) else: # Do we need to convert these into text_type as well? # return self._clean_data(to_text(variable._obj, nonstring='passthru')) return self._clean_data(variable._obj) try: if convert_bare: variable = self._convert_bare_variable(variable, bare_deprecated=bare_deprecated) if isinstance(variable, string_types): result = variable if self._contains_vars(variable): # Check to see if the string we are trying to render is just referencing a single # var. In this case we don't want to accidentally change the type of the variable # to a string by using the jinja template renderer. We just want to pass it. only_one = self.SINGLE_VAR.match(variable) if only_one: var_name = only_one.group(1) if var_name in self._available_variables: resolved_val = self._available_variables[var_name] if isinstance(resolved_val, NON_TEMPLATED_TYPES): return resolved_val elif resolved_val is None: return C.DEFAULT_NULL_REPRESENTATION # Using a cache in order to prevent template calls with already templated variables sha1_hash = None if cache: variable_hash = sha1(text_type(variable).encode('utf-8')) options_hash = sha1((text_type(preserve_trailing_newlines) + text_type(escape_backslashes) + text_type(fail_on_undefined) + text_type(overrides)).encode('utf-8')) sha1_hash = variable_hash.hexdigest() + options_hash.hexdigest() if cache and sha1_hash in self._cached_result: result = self._cached_result[sha1_hash] else: result = self._do_template(variable, preserve_trailing_newlines=preserve_trailing_newlines, escape_backslashes=escape_backslashes, fail_on_undefined=fail_on_undefined, overrides=overrides) if convert_data and not self._no_type_regex.match(variable): # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or \ result.startswith("[") or result in ("True", "False"): eval_results = safe_eval(result, locals=self._available_variables, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] else: # FIXME: if the safe_eval raised an error, should we do something with it? pass # we only cache in the case where we have a single variable # name, to make sure we're not putting things which may otherwise # be dynamic in the cache (filters, lookups, etc.) if cache: self._cached_result[sha1_hash] = result return result elif isinstance(variable, (list, tuple)): return [self.template(v, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) for v in variable] elif isinstance(variable, dict): d = {} # we don't use iteritems() here to avoid problems if the underlying dict # changes sizes due to the templating, which can happen with hostvars for k in variable.keys(): if k not in static_vars: d[k] = self.template(variable[k], preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) else: d[k] = variable[k] return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable
def test_set_literals(self): self.assertEqual(safe_eval('{0}'), set([0]))
def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' # Don't template unsafe variables, instead drop them back down to # their constituent type. if hasattr(variable, '__UNSAFE__'): if isinstance(variable, unicode): return unicode(variable) elif isinstance(variable, str): return str(variable) else: return variable try: if convert_bare: variable = self._convert_bare_variable(variable) if isinstance(variable, string_types): result = variable if self._contains_vars(variable): # Check to see if the string we are trying to render is just referencing a single # var. In this case we don't want to accidentally change the type of the variable # to a string by using the jinja template renderer. We just want to pass it. only_one = self.SINGLE_VAR.match(variable) if only_one: var_name = only_one.group(1) if var_name in self._available_variables: resolved_val = self._available_variables[var_name] if isinstance(resolved_val, NON_TEMPLATED_TYPES): return resolved_val elif resolved_val is None: return C.DEFAULT_NULL_REPRESENTATION result = self._do_template(variable, preserve_trailing_newlines=preserve_trailing_newlines, escape_backslashes=escape_backslashes, fail_on_undefined=fail_on_undefined, overrides=overrides) if convert_data: # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or \ result.startswith("[") or result in ("True", "False"): eval_results = safe_eval(result, locals=self._available_variables, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] else: # FIXME: if the safe_eval raised an error, should we do something with it? pass return result elif isinstance(variable, (list, tuple)): return [self.template(v, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) for v in variable] elif isinstance(variable, dict): d = {} # we don't use iteritems() here to avoid problems if the underlying dict # changes sizes due to the templating, which can happen with hostvars for k in variable.keys(): d[k] = self.template(variable[k], preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable
def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True): ''' Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. ''' # Don't template unsafe variables, instead drop them back down to # their constituent type. if hasattr(variable, '__UNSAFE__'): if isinstance(variable, text_type): return self._clean_data(text_type(variable)) elif isinstance(variable, binary_type): return self._clean_data(bytes(variable)) else: return self._clean_data(variable._obj) try: if convert_bare: variable = self._convert_bare_variable(variable) if isinstance(variable, string_types): result = variable if self._contains_vars(variable): # Check to see if the string we are trying to render is just referencing a single # var. In this case we don't want to accidentally change the type of the variable # to a string by using the jinja template renderer. We just want to pass it. only_one = self.SINGLE_VAR.match(variable) if only_one: var_name = only_one.group(1) if var_name in self._available_variables: resolved_val = self._available_variables[var_name] if isinstance(resolved_val, NON_TEMPLATED_TYPES): return resolved_val elif resolved_val is None: return C.DEFAULT_NULL_REPRESENTATION result = self._do_template( variable, preserve_trailing_newlines=preserve_trailing_newlines, escape_backslashes=escape_backslashes, fail_on_undefined=fail_on_undefined, overrides=overrides) if convert_data: # if this looks like a dictionary or list, convert it to such using the safe_eval method if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or \ result.startswith("[") or result in ("True", "False"): eval_results = safe_eval( result, locals=self._available_variables, include_exceptions=True) if eval_results[1] is None: result = eval_results[0] else: # FIXME: if the safe_eval raised an error, should we do something with it? pass #return self._clean_data(result) return result elif isinstance(variable, (list, tuple)): return [ self.template( v, preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) for v in variable ] elif isinstance(variable, dict): d = {} # we don't use iteritems() here to avoid problems if the underlying dict # changes sizes due to the templating, which can happen with hostvars for k in variable.keys(): d[k] = self.template( variable[k], preserve_trailing_newlines=preserve_trailing_newlines, fail_on_undefined=fail_on_undefined, overrides=overrides) return d else: return variable except AnsibleFilterError: if self._fail_on_filter_errors: raise else: return variable