diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 8c8990a97dc..fd174a8421e 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -34,6 +34,7 @@ from ansible.playbook.task import Task from ansible.template import Templar from ansible.utils.listify import listify_lookup_plugin_terms from ansible.utils.unicode import to_unicode +from ansible.utils.vars import json_variable_cleaner from ansible.utils.debug import debug @@ -123,7 +124,7 @@ class TaskExecutor: res['changed'] = False debug("dumping result to json") - result = json.dumps(res) + result = json.dumps(res, default=json_variable_cleaner) debug("done dumping result, returning") return result except AnsibleError as e: @@ -160,6 +161,11 @@ class TaskExecutor: else: raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop) + if items: + from ansible.vars.unsafe_proxy import UnsafeProxy + for idx, item in enumerate(items): + if item is not None and not isinstance(item, UnsafeProxy): + items[idx] = UnsafeProxy(item) return items def _run_loop(self, items): diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py index 6e2416dd044..55c841b231b 100644 --- a/lib/ansible/template/__init__.py +++ b/lib/ansible/template/__init__.py @@ -213,8 +213,15 @@ class Templar: 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__'): - return variable + if isinstance(variable, unicode): + return unicode(variable) + elif isinstance(variable, str): + return str(variable) + else: + return variable try: if convert_bare: @@ -313,8 +320,11 @@ class Templar: if self._fail_on_lookup_errors: raise ran = None + if ran: - ran = ",".join(ran) + from ansible.vars.unsafe_proxy import UnsafeProxy + ran = UnsafeProxy(",".join(ran)) + return ran else: raise AnsibleError("lookup plugin (%s) not found" % name) diff --git a/lib/ansible/utils/vars.py b/lib/ansible/utils/vars.py index d202a1b18fa..7d150e5fa5f 100644 --- a/lib/ansible/utils/vars.py +++ b/lib/ansible/utils/vars.py @@ -128,3 +128,14 @@ def isidentifier(ident): return True +def json_variable_cleaner(obj): + ''' + Used as the default= parameter to json.dumps(), this method helps + clear out UnsafeProxy variables so they can be properly JSON encoded + ''' + from ansible.vars.unsafe_proxy import UnsafeProxy + if isinstance(obj, UnsafeProxy): + return obj._obj + else: + return obj + diff --git a/lib/ansible/vars/unsafe_proxy.py b/lib/ansible/vars/unsafe_proxy.py index a8bca351ae3..65bbf988948 100644 --- a/lib/ansible/vars/unsafe_proxy.py +++ b/lib/ansible/vars/unsafe_proxy.py @@ -59,10 +59,15 @@ class UnsafeProxy(object): # proxying (special cases) # def __getattribute__(self, name): - if name == '__UNSAFE__': + if name == '_obj': + return object.__getattribute__(self, "_obj") + elif name == '__UNSAFE__': return True else: return getattr(object.__getattribute__(self, "_obj"), name) + + def __eq__(self, obj): + return object.__getattribute__(self, "_obj") == obj def __delattr__(self, name): delattr(object.__getattribute__(self, "_obj"), name) def __setattr__(self, name, value): @@ -108,7 +113,7 @@ class UnsafeProxy(object): namespace = {} for name in cls._special_names: - if hasattr(theclass, name): + if hasattr(theclass, name) and not hasattr(cls, name): namespace[name] = make_method(name) return type("%s(%s)" % (cls.__name__, theclass.__name__), (cls,), namespace)