# Buggy code
def main():
    """entry point for module execution"""
    argument_spec = dict(
        commands=dict(type="list"),
        rpcs=dict(type="list"),
        display=dict(choices=["text", "json", "xml"], aliases=["format", "output"]),
        wait_for=dict(type="list", aliases=["waitfor"]),
        match=dict(default="all", choices=["all", "any"]),
        retries=dict(default=10, type="int"),
        interval=dict(default=1, type="int"),
    )

    argument_spec.update(junos_argument_spec)

    required_one_of = [("commands", "rpcs")]

    module = AnsibleModule(
        argument_spec=argument_spec,
        required_one_of=required_one_of,
        supports_check_mode=True,
    )

    warnings = list()
    check_args(module, warnings)

    items = list()
    items.extend(parse_commands(module, warnings))
    items.extend(parse_rpcs(module))

    wait_for = module.params["wait_for"] or list()
    conditionals = [Conditional(c) for c in wait_for]

    retries = module.params["retries"]
    interval = module.params["interval"]
    match = module.params["match"]

    while retries > 0:
        responses = rpc(module, items)

        transformed = list()
        output = list()
        for item, resp in zip(items, responses):
            if item["xattrs"]["format"] == "xml":
                if not HAS_JXMLEASE:
                    module.fail_json(
                        msg="jxmlease is required but does not appear to be installed. "
                        "It can be installed using `pip install jxmlease`"
                    )

                try:
                    json_resp = jxmlease.parse(resp)
                    transformed.append(json_resp)
                    output.append(json_resp)
                except:
                    raise ValueError(resp)
            else:
                transformed.append(resp)

        for item in list(conditionals):
            try:
                if item(transformed):
                    if match == "any":
                        conditionals = list()
                        break
                    conditionals.remove(item)
            except FailedConditionalError:
                pass

        if not conditionals:
            break

        time.sleep(interval)
        retries -= 1

    if conditionals:
        failed_conditions = [item.raw for item in conditionals]
        msg = "One or more conditional statements have not be satisfied"
        module.fail_json(msg=msg, failed_conditions=failed_conditions)

    result = {
        "changed": False,
        "warnings": warnings,
        "stdout": responses,
        "stdout_lines": to_lines(responses),
    }

    if output:
        result["output"] = output

    module.exit_json(**result)


# Traceback (AttributeError)
$ ansible-playbook dev-conf.yml --extra-vars "dev=athens os=juniper" -vvvv
Using /home/app/saint/ansible/ansible.cfg as config file
Loading callback plugin default of type stdout, v2.0 from /usr/lib/python2.7/site-packages/ansible/plugins/callback/__init__.pyc

PLAYBOOK: dev-conf.yml *******************************************************************************************************************************
1 plays in dev-conf.yml

PLAY [dev-conf] **************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/setup.py
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: app
<localhost> EXEC /bin/sh -c 'echo ~ &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 &amp;&amp; mkdir -p "` echo /home/app/.ansible/tmp/ansible-tmp-1499090338.08-16740590805618 `" &amp;&amp; echo ansible-tmp-1499090338.08-16740590805618="` echo /home/app/.ansible/tmp/ansible-tmp-1499090338.08-16740590805618 `" ) &amp;&amp; sleep 0'
<localhost> PUT /tmp/tmpMTpD5T TO /home/app/.ansible/tmp/ansible-tmp-1499090338.08-16740590805618/setup.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/app/.ansible/tmp/ansible-tmp-1499090338.08-16740590805618/ /home/app/.ansible/tmp/ansible-tmp-1499090338.08-16740590805618/setup.py &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /home/app/.ansible/tmp/ansible-tmp-1499090338.08-16740590805618/setup.py; rm -rf "/home/app/.ansible/tmp/ansible-tmp-1499090338.08-16740590805618/" > /dev/null 2>&amp;1 &amp;&amp; sleep 0'
ok: [localhost]
META: ran handlers

TASK [Retrieve router's configuration: junos] ********************************************************************************************************
task path: /home/app/saint/ansible/dev-conf.yml:12
<athens> using connection plugin netconf
<athens> socket_path: /home/app/.ansible/pc/d090e15c31
open_session() returned 0 ok
Using module file /usr/lib/python2.7/site-packages/ansible/modules/network/junos/junos_command.py
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: app
<localhost> EXEC /bin/sh -c 'echo ~ &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 &amp;&amp; mkdir -p "` echo /home/app/.ansible/tmp/ansible-tmp-1499090340.43-6328802200596 `" &amp;&amp; echo ansible-tmp-1499090340.43-6328802200596="` echo /home/app/.ansible/tmp/ansible-tmp-1499090340.43-6328802200596 `" ) &amp;&amp; sleep 0'
<localhost> PUT /tmp/tmpGOR9Z2 TO /home/app/.ansible/tmp/ansible-tmp-1499090340.43-6328802200596/junos_command.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/app/.ansible/tmp/ansible-tmp-1499090340.43-6328802200596/ /home/app/.ansible/tmp/ansible-tmp-1499090340.43-6328802200596/junos_command.py &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /home/app/.ansible/tmp/ansible-tmp-1499090340.43-6328802200596/junos_command.py; rm -rf "/home/app/.ansible/tmp/ansible-tmp-1499090340.43-6328802200596/" > /dev/null 2>&amp;1 &amp;&amp; sleep 0'
The full traceback is:
Traceback (most recent call last):
File "/tmp/ansible_t7Cbbz/ansible_module_junos_command.py", line 388, in <module>
main()
File "/tmp/ansible_t7Cbbz/ansible_module_junos_command.py", line 338, in main
responses = rpc(module, items)
File "/tmp/ansible_t7Cbbz/ansible_module_junos_command.py", line 229, in rpc
responses.append(data.text.strip())
AttributeError: 'NoneType' object has no attribute 'text'

fatal: [localhost]: FAILED! => {
"changed": false,
"failed": true,
"module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_t7Cbbz/ansible_module_junos_command.py\", line 388, in <module>\n    main()\n  File \"/tmp/ansible_t7Cbbz/ansible_module_junos_command.py\", line 338, in main\n    responses = rpc(module, items)\n  File \"/tmp/ansible_t7Cbbz/ansible_module_junos_command.py\", line 229, in rpc\n    responses.append(data.text.strip())\nAttributeError: 'NoneType' object has no attribute 'text'\n",
"module_stdout": "",
"msg": "MODULE FAILURE",
"rc": 0
}
to retry, use: --limit @/home/app/saint/ansible/dev-conf.retry

PLAY RECAP *******************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1

# Fixed code
def main():
    """entry point for module execution"""
    argument_spec = dict(
        commands=dict(type="list"),
        rpcs=dict(type="list"),
        display=dict(
            choices=["text", "json", "xml", "set"], aliases=["format", "output"]
        ),
        wait_for=dict(type="list", aliases=["waitfor"]),
        match=dict(default="all", choices=["all", "any"]),
        retries=dict(default=10, type="int"),
        interval=dict(default=1, type="int"),
    )

    argument_spec.update(junos_argument_spec)

    required_one_of = [("commands", "rpcs")]

    module = AnsibleModule(
        argument_spec=argument_spec,
        required_one_of=required_one_of,
        supports_check_mode=True,
    )

    warnings = list()
    check_args(module, warnings)

    items = list()
    items.extend(parse_commands(module, warnings))
    items.extend(parse_rpcs(module))

    wait_for = module.params["wait_for"] or list()
    conditionals = [Conditional(c) for c in wait_for]

    retries = module.params["retries"]
    interval = module.params["interval"]
    match = module.params["match"]

    while retries > 0:
        responses = rpc(module, items)
        transformed = list()
        output = list()
        for item, resp in zip(items, responses):
            if item["xattrs"]["format"] == "xml":
                if not HAS_JXMLEASE:
                    module.fail_json(
                        msg="jxmlease is required but does not appear to be installed. "
                        "It can be installed using `pip install jxmlease`"
                    )

                try:
                    json_resp = jxmlease.parse(resp)
                    transformed.append(json_resp)
                    output.append(json_resp)
                except:
                    raise ValueError(resp)
            else:
                transformed.append(resp)

        for item in list(conditionals):
            try:
                if item(transformed):
                    if match == "any":
                        conditionals = list()
                        break
                    conditionals.remove(item)
            except FailedConditionalError:
                pass

        if not conditionals:
            break

        time.sleep(interval)
        retries -= 1

    if conditionals:
        failed_conditions = [item.raw for item in conditionals]
        msg = "One or more conditional statements have not be satisfied"
        module.fail_json(msg=msg, failed_conditions=failed_conditions)

    result = {
        "changed": False,
        "warnings": warnings,
        "stdout": responses,
        "stdout_lines": to_lines(responses),
    }

    if output:
        result["output"] = output

    module.exit_json(**result)