We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Exception with `ansible_port` on delegate task result
##### SUMMARY
If we have a task that delegate to localhost, we have an exception thrown : 

````
ERROR! Unexpected Exception, this is probably a bug: 'ansible_port'
````

##### ISSUE TYPE
This seems to be related to #42577. Reverting this commit fix the issue.

##### COMPONENT NAME
delegate_to

##### ANSIBLE VERSION
```
ansible 2.6.1.post0
  config file = /opt/monitoring/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.15rc1 (default, Apr 15 2018, 21:51:34) [GCC 7.3.0]
```

##### CONFIGURATION
````
DEFAULT_ROLES_PATH(/opt/monitoring/ansible/ansible.cfg) = [u'/opt/monitoring/ansible/.galaxy_roles']
````

##### OS / ENVIRONMENT
Ubuntu 18.04

##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- hosts: '*'
  tasks:
    - name: Write gossip encryption key locally for use with new servers
      copy:
        content: "{{ consul_raw_key }}"
        dest: '/tmp/consul_raw.key'
      become: no
      no_log: true
      run_once: true
      register: consul_local_key
      delegate_to: localhost
      changed_when: false
      when: consul_raw_key is defined
```

##### EXPECTED RESULTS
Something working, not an exception
##### ACTUAL RESULTS
```
TASK [Write gossip encryption key locally for use with new servers] ******************************************************************************************************
task path: /opt/monitoring/ansible/playbook.yml:8
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root
<localhost> EXEC /bin/sh -c 'echo ~root && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818 `" && echo ansible-tmp-1532011875.95-252544437995818="` echo /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818 `" ) && sleep 0'
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/files/stat.py
<localhost> PUT /root/.ansible/tmp/ansible-local-18955F96q_C/tmplKd6qB TO /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/stat.py
<localhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/ /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/stat.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/stat.py && sleep 0'
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/files/file.py
<localhost> PUT /root/.ansible/tmp/ansible-local-18955F96q_C/tmpFYSMGp TO /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/file.py
<localhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/ /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/file.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/file.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/ > /dev/null 2>&1 && sleep 0'
ERROR! Unexpected Exception, this is probably a bug: 'ansible_port'
the full traceback was:

Traceback (most recent call last):
  File "/usr/local/bin/ansible-playbook", line 118, in <module>
    exit_code = cli.run()
  File "/usr/local/lib/python2.7/dist-packages/ansible/cli/playbook.py", line 122, in run
    results = pbex.run()
  File "/usr/local/lib/python2.7/dist-packages/ansible/executor/playbook_executor.py", line 159, in run
    result = self._tqm.run(play=play)
  File "/usr/local/lib/python2.7/dist-packages/ansible/executor/task_queue_manager.py", line 289, in run
    play_return = strategy.run(iterator, play_context)
  File "/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/linear.py", line 323, in run
    results += self._wait_on_pending_results(iterator)
  File "/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/__init__.py", line 674, in _wait_on_pending_results
    results = self._process_pending_results(iterator)
  File "/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/__init__.py", line 117, in inner
    results = func(self, iterator, one_pass=one_pass, max_passes=max_passes)
  File "/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/__init__.py", line 636, in _process_pending_results
    self._tqm.send_callback('v2_runner_on_ok', task_result)
  File "/usr/local/lib/python2.7/dist-packages/ansible/executor/task_queue_manager.py", line 366, in send_callback
    new_args.append(arg.clean_copy())
  File "/usr/local/lib/python2.7/dist-packages/ansible/executor/task_result.py", line 127, in clean_copy
    x[sub][key] = self._result[sub][key]
KeyError: 'ansible_port'
```


The error is in #42577

--- 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/executor/task_result.py`
Content:
```
1 # Copyright: (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
2 
3 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
4 
5 from __future__ import (absolute_import, division, print_function)
6 __metaclass__ = type
7 
8 from copy import deepcopy
9 
10 from ansible import constants as C
11 from ansible.parsing.dataloader import DataLoader
12 from ansible.vars.clean import strip_internal_keys
13 
14 _IGNORE = ('failed', 'skipped')
15 _PRESERVE = ('attempts', 'changed', 'retries')
16 _SUB_PRESERVE = {'_ansible_delegated_vars': ('ansible_host', 'ansible_port', 'ansible_user', 'ansible_connection')}
17 
18 
19 class TaskResult:
20     '''
21     This class is responsible for interpreting the resulting data
22     from an executed task, and provides helper methods for determining
23     the result of a given task.
24     '''
25 
26     def __init__(self, host, task, return_data, task_fields=None):
27         self._host = host
28         self._task = task
29 
30         if isinstance(return_data, dict):
31             self._result = return_data.copy()
32         else:
33             self._result = DataLoader().load(return_data)
34 
35         if task_fields is None:
36             self._task_fields = dict()
37         else:
38             self._task_fields = task_fields
39 
40     @property
41     def task_name(self):
42         return self._task_fields.get('name', None) or self._task.get_name()
43 
44     def is_changed(self):
45         return self._check_key('changed')
46 
47     def is_skipped(self):
48         # loop results
49         if 'results' in self._result:
50             results = self._result['results']
51             # Loop tasks are only considered skipped if all items were skipped.
52             # some squashed results (eg, yum) are not dicts and can't be skipped individually
53             if results and all(isinstance(res, dict) and res.get('skipped', False) for res in results):
54                 return True
55 
56         # regular tasks and squashed non-dict results
57         return self._result.get('skipped', False)
58 
59     def is_failed(self):
60         if 'failed_when_result' in self._result or \
61            'results' in self._result and True in [True for x in self._result['results'] if 'failed_when_result' in x]:
62             return self._check_key('failed_when_result')
63         else:
64             return self._check_key('failed')
65 
66     def is_unreachable(self):
67         return self._check_key('unreachable')
68 
69     def needs_debugger(self, globally_enabled=False):
70         _debugger = self._task_fields.get('debugger')
71         _ignore_errors = C.TASK_DEBUGGER_IGNORE_ERRORS and self._task_fields.get('ignore_errors')
72 
73         ret = False
74         if globally_enabled and ((self.is_failed() and not _ignore_errors) or self.is_unreachable()):
75             ret = True
76 
77         if _debugger in ('always',):
78             ret = True
79         elif _debugger in ('never',):
80             ret = False
81         elif _debugger in ('on_failed',) and self.is_failed() and not _ignore_errors:
82             ret = True
83         elif _debugger in ('on_unreachable',) and self.is_unreachable():
84             ret = True
85         elif _debugger in('on_skipped',) and self.is_skipped():
86             ret = True
87 
88         return ret
89 
90     def _check_key(self, key):
91         '''get a specific key from the result or its items'''
92 
93         if isinstance(self._result, dict) and key in self._result:
94             return self._result.get(key, False)
95         else:
96             flag = False
97             for res in self._result.get('results', []):
98                 if isinstance(res, dict):
99                     flag |= res.get(key, False)
100             return flag
101 
102     def clean_copy(self):
103 
104         ''' returns 'clean' taskresult object '''
105 
106         # FIXME: clean task_fields, _task and _host copies
107         result = TaskResult(self._host, self._task, {}, self._task_fields)
108 
109         # statuses are already reflected on the event type
110         if result._task and result._task.action in ['debug']:
111             # debug is verbose by default to display vars, no need to add invocation
112             ignore = _IGNORE + ('invocation',)
113         else:
114             ignore = _IGNORE
115 
116         if self._task.no_log or self._result.get('_ansible_no_log', False):
117             x = {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result"}
118 
119             # preserve full
120             for preserve in _PRESERVE:
121                 if preserve in self._result:
122                     x[preserve] = self._result[preserve]
123 
124             # preserve subset
125             for sub in _SUB_PRESERVE:
126                 if sub in self._result:
127                     x[sub] = {}
128                     for key in _SUB_PRESERVE[sub]:
129                         x[sub][key] = self._result[sub][key]
130 
131             result._result = x
132         elif self._result:
133             result._result = deepcopy(self._result)
134 
135             # actualy remove
136             for remove_key in ignore:
137                 if remove_key in result._result:
138                     del result._result[remove_key]
139 
140             # remove almost ALL internal keys, keep ones relevant to callback
141             strip_internal_keys(result._result, exceptions=('_ansible_verbose_always', '_ansible_item_label', '_ansible_no_log'))
142 
143         return result
144
```
--- 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/executor/task_result.py b/lib/ansible/executor/task_result.py
--- a/lib/ansible/executor/task_result.py
+++ b/lib/ansible/executor/task_result.py
@@ -126,7 +126,8 @@
                 if sub in self._result:
                     x[sub] = {}
                     for key in _SUB_PRESERVE[sub]:
-                        x[sub][key] = self._result[sub][key]
+                        if key in self._result[sub]:
+                            x[sub][key] = self._result[sub][key]
 
             result._result = x
         elif self._result:

{"golden_diff": "diff --git a/lib/ansible/executor/task_result.py b/lib/ansible/executor/task_result.py\n--- a/lib/ansible/executor/task_result.py\n+++ b/lib/ansible/executor/task_result.py\n@@ -126,7 +126,8 @@\n                 if sub in self._result:\n                     x[sub] = {}\n                     for key in _SUB_PRESERVE[sub]:\n-                        x[sub][key] = self._result[sub][key]\n+                        if key in self._result[sub]:\n+                            x[sub][key] = self._result[sub][key]\n \n             result._result = x\n         elif self._result:\n", "issue": "Exception with `ansible_port` on delegate task result\n##### SUMMARY\r\nIf we have a task that delegate to localhost, we have an exception thrown : \r\n\r\n````\r\nERROR! Unexpected Exception, this is probably a bug: 'ansible_port'\r\n````\r\n\r\n##### ISSUE TYPE\r\nThis seems to be related to #42577. Reverting this commit fix the issue.\r\n\r\n##### COMPONENT NAME\r\ndelegate_to\r\n\r\n##### ANSIBLE VERSION\r\n```\r\nansible 2.6.1.post0\r\n  config file = /opt/monitoring/ansible/ansible.cfg\r\n  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible\r\n  executable location = /usr/local/bin/ansible\r\n  python version = 2.7.15rc1 (default, Apr 15 2018, 21:51:34) [GCC 7.3.0]\r\n```\r\n\r\n##### CONFIGURATION\r\n````\r\nDEFAULT_ROLES_PATH(/opt/monitoring/ansible/ansible.cfg) = [u'/opt/monitoring/ansible/.galaxy_roles']\r\n````\r\n\r\n##### OS / ENVIRONMENT\r\nUbuntu 18.04\r\n\r\n##### STEPS TO REPRODUCE\r\n<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.\r\nFor new features, show how the feature would be used. -->\r\n\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n```yaml\r\n- hosts: '*'\r\n  tasks:\r\n    - name: Write gossip encryption key locally for use with new servers\r\n      copy:\r\n        content: \"{{ consul_raw_key }}\"\r\n        dest: '/tmp/consul_raw.key'\r\n      become: no\r\n      no_log: true\r\n      run_once: true\r\n      register: consul_local_key\r\n      delegate_to: localhost\r\n      changed_when: false\r\n      when: consul_raw_key is defined\r\n```\r\n\r\n##### EXPECTED RESULTS\r\nSomething working, not an exception\r\n##### ACTUAL RESULTS\r\n```\r\nTASK [Write gossip encryption key locally for use with new servers] ******************************************************************************************************\r\ntask path: /opt/monitoring/ansible/playbook.yml:8\r\n<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root\r\n<localhost> EXEC /bin/sh -c 'echo ~root && sleep 0'\r\n<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p \"` echo /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818 `\" && echo ansible-tmp-1532011875.95-252544437995818=\"` echo /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818 `\" ) && sleep 0'\r\nUsing module file /usr/local/lib/python2.7/dist-packages/ansible/modules/files/stat.py\r\n<localhost> PUT /root/.ansible/tmp/ansible-local-18955F96q_C/tmplKd6qB TO /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/stat.py\r\n<localhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/ /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/stat.py && sleep 0'\r\n<localhost> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/stat.py && sleep 0'\r\nUsing module file /usr/local/lib/python2.7/dist-packages/ansible/modules/files/file.py\r\n<localhost> PUT /root/.ansible/tmp/ansible-local-18955F96q_C/tmpFYSMGp TO /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/file.py\r\n<localhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/ /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/file.py && sleep 0'\r\n<localhost> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/file.py && sleep 0'\r\n<localhost> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1532011875.95-252544437995818/ > /dev/null 2>&1 && sleep 0'\r\nERROR! Unexpected Exception, this is probably a bug: 'ansible_port'\r\nthe full traceback was:\r\n\r\nTraceback (most recent call last):\r\n  File \"/usr/local/bin/ansible-playbook\", line 118, in <module>\r\n    exit_code = cli.run()\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/cli/playbook.py\", line 122, in run\r\n    results = pbex.run()\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/executor/playbook_executor.py\", line 159, in run\r\n    result = self._tqm.run(play=play)\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/executor/task_queue_manager.py\", line 289, in run\r\n    play_return = strategy.run(iterator, play_context)\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/linear.py\", line 323, in run\r\n    results += self._wait_on_pending_results(iterator)\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/__init__.py\", line 674, in _wait_on_pending_results\r\n    results = self._process_pending_results(iterator)\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/__init__.py\", line 117, in inner\r\n    results = func(self, iterator, one_pass=one_pass, max_passes=max_passes)\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/plugins/strategy/__init__.py\", line 636, in _process_pending_results\r\n    self._tqm.send_callback('v2_runner_on_ok', task_result)\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/executor/task_queue_manager.py\", line 366, in send_callback\r\n    new_args.append(arg.clean_copy())\r\n  File \"/usr/local/lib/python2.7/dist-packages/ansible/executor/task_result.py\", line 127, in clean_copy\r\n    x[sub][key] = self._result[sub][key]\r\nKeyError: 'ansible_port'\r\n```\r\n\r\n\r\nThe error is in #42577\n", "before_files": [{"content": "# Copyright: (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>\n\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nfrom copy import deepcopy\n\nfrom ansible import constants as C\nfrom ansible.parsing.dataloader import DataLoader\nfrom ansible.vars.clean import strip_internal_keys\n\n_IGNORE = ('failed', 'skipped')\n_PRESERVE = ('attempts', 'changed', 'retries')\n_SUB_PRESERVE = {'_ansible_delegated_vars': ('ansible_host', 'ansible_port', 'ansible_user', 'ansible_connection')}\n\n\nclass TaskResult:\n    '''\n    This class is responsible for interpreting the resulting data\n    from an executed task, and provides helper methods for determining\n    the result of a given task.\n    '''\n\n    def __init__(self, host, task, return_data, task_fields=None):\n        self._host = host\n        self._task = task\n\n        if isinstance(return_data, dict):\n            self._result = return_data.copy()\n        else:\n            self._result = DataLoader().load(return_data)\n\n        if task_fields is None:\n            self._task_fields = dict()\n        else:\n            self._task_fields = task_fields\n\n    @property\n    def task_name(self):\n        return self._task_fields.get('name', None) or self._task.get_name()\n\n    def is_changed(self):\n        return self._check_key('changed')\n\n    def is_skipped(self):\n        # loop results\n        if 'results' in self._result:\n            results = self._result['results']\n            # Loop tasks are only considered skipped if all items were skipped.\n            # some squashed results (eg, yum) are not dicts and can't be skipped individually\n            if results and all(isinstance(res, dict) and res.get('skipped', False) for res in results):\n                return True\n\n        # regular tasks and squashed non-dict results\n        return self._result.get('skipped', False)\n\n    def is_failed(self):\n        if 'failed_when_result' in self._result or \\\n           'results' in self._result and True in [True for x in self._result['results'] if 'failed_when_result' in x]:\n            return self._check_key('failed_when_result')\n        else:\n            return self._check_key('failed')\n\n    def is_unreachable(self):\n        return self._check_key('unreachable')\n\n    def needs_debugger(self, globally_enabled=False):\n        _debugger = self._task_fields.get('debugger')\n        _ignore_errors = C.TASK_DEBUGGER_IGNORE_ERRORS and self._task_fields.get('ignore_errors')\n\n        ret = False\n        if globally_enabled and ((self.is_failed() and not _ignore_errors) or self.is_unreachable()):\n            ret = True\n\n        if _debugger in ('always',):\n            ret = True\n        elif _debugger in ('never',):\n            ret = False\n        elif _debugger in ('on_failed',) and self.is_failed() and not _ignore_errors:\n            ret = True\n        elif _debugger in ('on_unreachable',) and self.is_unreachable():\n            ret = True\n        elif _debugger in('on_skipped',) and self.is_skipped():\n            ret = True\n\n        return ret\n\n    def _check_key(self, key):\n        '''get a specific key from the result or its items'''\n\n        if isinstance(self._result, dict) and key in self._result:\n            return self._result.get(key, False)\n        else:\n            flag = False\n            for res in self._result.get('results', []):\n                if isinstance(res, dict):\n                    flag |= res.get(key, False)\n            return flag\n\n    def clean_copy(self):\n\n        ''' returns 'clean' taskresult object '''\n\n        # FIXME: clean task_fields, _task and _host copies\n        result = TaskResult(self._host, self._task, {}, self._task_fields)\n\n        # statuses are already reflected on the event type\n        if result._task and result._task.action in ['debug']:\n            # debug is verbose by default to display vars, no need to add invocation\n            ignore = _IGNORE + ('invocation',)\n        else:\n            ignore = _IGNORE\n\n        if self._task.no_log or self._result.get('_ansible_no_log', False):\n            x = {\"censored\": \"the output has been hidden due to the fact that 'no_log: true' was specified for this result\"}\n\n            # preserve full\n            for preserve in _PRESERVE:\n                if preserve in self._result:\n                    x[preserve] = self._result[preserve]\n\n            # preserve subset\n            for sub in _SUB_PRESERVE:\n                if sub in self._result:\n                    x[sub] = {}\n                    for key in _SUB_PRESERVE[sub]:\n                        x[sub][key] = self._result[sub][key]\n\n            result._result = x\n        elif self._result:\n            result._result = deepcopy(self._result)\n\n            # actualy remove\n            for remove_key in ignore:\n                if remove_key in result._result:\n                    del result._result[remove_key]\n\n            # remove almost ALL internal keys, keep ones relevant to callback\n            strip_internal_keys(result._result, exceptions=('_ansible_verbose_always', '_ansible_item_label', '_ansible_no_log'))\n\n        return result\n", "path": "lib/ansible/executor/task_result.py"}], "after_files": [{"content": "# Copyright: (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>\n\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nfrom copy import deepcopy\n\nfrom ansible import constants as C\nfrom ansible.parsing.dataloader import DataLoader\nfrom ansible.vars.clean import strip_internal_keys\n\n_IGNORE = ('failed', 'skipped')\n_PRESERVE = ('attempts', 'changed', 'retries')\n_SUB_PRESERVE = {'_ansible_delegated_vars': ('ansible_host', 'ansible_port', 'ansible_user', 'ansible_connection')}\n\n\nclass TaskResult:\n    '''\n    This class is responsible for interpreting the resulting data\n    from an executed task, and provides helper methods for determining\n    the result of a given task.\n    '''\n\n    def __init__(self, host, task, return_data, task_fields=None):\n        self._host = host\n        self._task = task\n\n        if isinstance(return_data, dict):\n            self._result = return_data.copy()\n        else:\n            self._result = DataLoader().load(return_data)\n\n        if task_fields is None:\n            self._task_fields = dict()\n        else:\n            self._task_fields = task_fields\n\n    @property\n    def task_name(self):\n        return self._task_fields.get('name', None) or self._task.get_name()\n\n    def is_changed(self):\n        return self._check_key('changed')\n\n    def is_skipped(self):\n        # loop results\n        if 'results' in self._result:\n            results = self._result['results']\n            # Loop tasks are only considered skipped if all items were skipped.\n            # some squashed results (eg, yum) are not dicts and can't be skipped individually\n            if results and all(isinstance(res, dict) and res.get('skipped', False) for res in results):\n                return True\n\n        # regular tasks and squashed non-dict results\n        return self._result.get('skipped', False)\n\n    def is_failed(self):\n        if 'failed_when_result' in self._result or \\\n           'results' in self._result and True in [True for x in self._result['results'] if 'failed_when_result' in x]:\n            return self._check_key('failed_when_result')\n        else:\n            return self._check_key('failed')\n\n    def is_unreachable(self):\n        return self._check_key('unreachable')\n\n    def needs_debugger(self, globally_enabled=False):\n        _debugger = self._task_fields.get('debugger')\n        _ignore_errors = C.TASK_DEBUGGER_IGNORE_ERRORS and self._task_fields.get('ignore_errors')\n\n        ret = False\n        if globally_enabled and ((self.is_failed() and not _ignore_errors) or self.is_unreachable()):\n            ret = True\n\n        if _debugger in ('always',):\n            ret = True\n        elif _debugger in ('never',):\n            ret = False\n        elif _debugger in ('on_failed',) and self.is_failed() and not _ignore_errors:\n            ret = True\n        elif _debugger in ('on_unreachable',) and self.is_unreachable():\n            ret = True\n        elif _debugger in('on_skipped',) and self.is_skipped():\n            ret = True\n\n        return ret\n\n    def _check_key(self, key):\n        '''get a specific key from the result or its items'''\n\n        if isinstance(self._result, dict) and key in self._result:\n            return self._result.get(key, False)\n        else:\n            flag = False\n            for res in self._result.get('results', []):\n                if isinstance(res, dict):\n                    flag |= res.get(key, False)\n            return flag\n\n    def clean_copy(self):\n\n        ''' returns 'clean' taskresult object '''\n\n        # FIXME: clean task_fields, _task and _host copies\n        result = TaskResult(self._host, self._task, {}, self._task_fields)\n\n        # statuses are already reflected on the event type\n        if result._task and result._task.action in ['debug']:\n            # debug is verbose by default to display vars, no need to add invocation\n            ignore = _IGNORE + ('invocation',)\n        else:\n            ignore = _IGNORE\n\n        if self._task.no_log or self._result.get('_ansible_no_log', False):\n            x = {\"censored\": \"the output has been hidden due to the fact that 'no_log: true' was specified for this result\"}\n\n            # preserve full\n            for preserve in _PRESERVE:\n                if preserve in self._result:\n                    x[preserve] = self._result[preserve]\n\n            # preserve subset\n            for sub in _SUB_PRESERVE:\n                if sub in self._result:\n                    x[sub] = {}\n                    for key in _SUB_PRESERVE[sub]:\n                        if key in self._result[sub]:\n                            x[sub][key] = self._result[sub][key]\n\n            result._result = x\n        elif self._result:\n            result._result = deepcopy(self._result)\n\n            # actualy remove\n            for remove_key in ignore:\n                if remove_key in result._result:\n                    del result._result[remove_key]\n\n            # remove almost ALL internal keys, keep ones relevant to callback\n            strip_internal_keys(result._result, exceptions=('_ansible_verbose_always', '_ansible_item_label', '_ansible_no_log'))\n\n        return result\n", "path": "lib/ansible/executor/task_result.py"}]}