Repository: ansible/ansible

Issue:
UnboundLocalError when trying to use result['deprecations']
While using `result['deprecations'] = 'Some deprecation warning` in module, I am getting following error

```
# ansible -m demo -a "state=present passwordfile=/etc/hosts src=/tmp"  localhost
 [WARNING]: provided hosts list is empty, only localhost is available

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: UnboundLocalError: local variable 'd' referenced before assignment
localhost | FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_eio1be8x/ansible_module_demo.py\", line 21, in <module>\n    main()\n  File \"/tmp/ansible_eio1be8x/ansible_module_demo.py\", line 18, in main\n    module.exit_json(**result)\n  File \"/tmp/ansible_eio1be8x/ansible_modlib.zip/ansible/module_utils/basic.py\", line 2017, in exit_json\n  File \"/tmp/ansible_eio1be8x/ansible_modlib.zip/ansible/module_utils/basic.py\", line 2002, in _return_formatted\nUnboundLocalError: local variable 'd' referenced before assignment\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 0
}
```
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ansible/module_utils/basic.py

##### ANSIBLE VERSION
```
2.4 devel
```

##### CONFIGURATION
Default - no changes

##### OS / ENVIRONMENT
Git devel branch 
Fedora 25 

##### SUMMARY
Ansible throws error while using `deprecations` key in result 

##### STEPS TO REPRODUCE
```
from ansible.module_utils.basic import AnsibleModule

def main():
    module = AnsibleModule(
                argument_spec=dict(
                        state=dict(default='present'),
                        src=dict(type='path'),
                        password=dict(type='str', no_log=True),
                        passwordfile=dict(type='path'),
                        dest=dict(),
                ),
                required_if=[('state', 'present', ('src',))],
                required_one_of=[['password', 'passwordfile']],
                mutually_exclusive=[['password', 'passwordfile']],
            )
    result = dict(changed=False)
    result['deprecations'] = 'Sample DEPRECATION Warning message'
    module.exit_json(**result)

if __name__ == "__main__":
    main()
```

Command to use above `demo.py` module

```
ansible -m demo -a "state=present passwordfile=/etc/hosts src=/tmp"  localhost
```

##### EXPECTED RESULTS
Playbook should show below output
```
# ansible -m demo -a "state=present passwordfile=/etc/hosts src=/tmp"  localhost
 [WARNING]: provided hosts list is empty, only localhost is available

[DEPRECATION WARNING]: Sample DEPRECATION Warning message.
This feature will be removed in a future release. Deprecation warnings can be
disabled by setting deprecation_warnings=False in ansible.cfg.
localhost | SUCCESS => {
    "changed": false
}
```

##### ACTUAL RESULTS
```
UnboundLocalError: local variable 'd' referenced before assignment
```


Discussion:

Resolving patch:
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -1999,7 +1999,7 @@ def _return_formatted(self, kwargs):
                     else:
                         self.deprecate(d)
             else:
-                self.deprecate(d)
+                self.deprecate(kwargs['deprecations'])
 
         if self._deprecations:
             kwargs['deprecations'] = self._deprecations


Test patch:
diff --git a/test/units/module_utils/basic/test_deprecate_warn.py b/test/units/module_utils/basic/test_deprecate_warn.py
--- a/test/units/module_utils/basic/test_deprecate_warn.py
+++ b/test/units/module_utils/basic/test_deprecate_warn.py
@@ -69,3 +69,21 @@ def test_deprecate(self):
                     {u'msg': u'deprecation4', u'version': '2.4'},
                 ])
 
+    def test_deprecate_without_list(self):
+        args = json.dumps(dict(ANSIBLE_MODULE_ARGS={}))
+        with swap_stdin_and_argv(stdin_data=args):
+            with swap_stdout():
+
+                ansible.module_utils.basic._ANSIBLE_ARGS = None
+                am = ansible.module_utils.basic.AnsibleModule(
+                    argument_spec = dict(),
+                )
+                am._name = 'unittest'
+
+                with self.assertRaises(SystemExit):
+                    am.exit_json(deprecations='Simple deprecation warning')
+                output = json.loads(sys.stdout.getvalue())
+                self.assertTrue('warnings' not in output or output['warnings'] == [])
+                self.assertEquals(output['deprecations'], [
+                    {u'msg': u'Simple deprecation warning', u'version': None},
+                ])