# Buggy code
def main():
    module = AnsibleModule(
        argument_spec=dict(
            command=dict(required=True),
            chdir=dict(type="path"),
            creates=dict(type="path"),
            removes=dict(type="path"),
            responses=dict(type="dict", required=True),
            timeout=dict(type="int", default=30),
            echo=dict(type="bool", default=False),
        )
    )

    if not HAS_PEXPECT:
        module.fail_json(msg=missing_required_lib("pexpect"), exception=PEXPECT_IMP_ERR)

    chdir = module.params["chdir"]
    args = module.params["command"]
    creates = module.params["creates"]
    removes = module.params["removes"]
    responses = module.params["responses"]
    timeout = module.params["timeout"]
    echo = module.params["echo"]

    events = dict()
    for key, value in responses.items():
        if isinstance(value, list):
            response = response_closure(module, key, value)
        else:
            response = "%s\n" % to_text(value).rstrip("\n")

        events[to_text(key)] = response

    if args.strip() == "":
        module.fail_json(rc=256, msg="no command given")

    if chdir:
        chdir = os.path.abspath(chdir)
        os.chdir(chdir)

    if creates:
        # do not run the command if the line contains creates=filename
        # and the filename already exists.  This allows idempotence
        # of command executions.
        if os.path.exists(creates):
            module.exit_json(
                cmd=args,
                stdout="skipped, since %s exists" % creates,
                changed=False,
                rc=0,
            )

    if removes:
        # do not run the command if the line contains removes=filename
        # and the filename does not exist.  This allows idempotence
        # of command executions.
        if not os.path.exists(removes):
            module.exit_json(
                cmd=args,
                stdout="skipped, since %s does not exist" % removes,
                changed=False,
                rc=0,
            )

    startd = datetime.datetime.now()

    try:
        try:
            # Prefer pexpect.run from pexpect>=4
            out, rc = pexpect.run(
                args,
                timeout=timeout,
                withexitstatus=True,
                events=events,
                cwd=chdir,
                echo=echo,
                encoding="utf-8",
            )
        except TypeError:
            # Use pexpect.runu in pexpect>=3.3,<4
            out, rc = pexpect.runu(
                args,
                timeout=timeout,
                withexitstatus=True,
                events=events,
                cwd=chdir,
                echo=echo,
            )
    except (TypeError, AttributeError) as e:
        # This should catch all insufficient versions of pexpect
        # We deem them insufficient for their lack of ability to specify
        # to not echo responses via the run/runu functions, which would
        # potentially leak sensitive information
        module.fail_json(
            msg="Insufficient version of pexpect installed "
            "(%s), this module requires pexpect>=3.3. "
            "Error was %s" % (pexpect.__version__, to_native(e))
        )
    except pexpect.ExceptionPexpect as e:
        module.fail_json(msg="%s" % to_native(e), exception=traceback.format_exc())

    endd = datetime.datetime.now()
    delta = endd - startd

    if out is None:
        out = ""

    result = dict(
        cmd=args,
        stdout=out.rstrip("\r\n"),
        rc=rc,
        start=str(startd),
        end=str(endd),
        delta=str(delta),
        changed=True,
    )

    if rc is None:
        module.fail_json(msg="command exceeded timeout", **result)
    elif rc != 0:
        module.fail_json(msg="non-zero return code", **result)

    module.exit_json(**result)


# Traceback (nUnicodeDecodeError)
fatal: [10.135.232.213]: FAILED! => {"changed": false, "failed": true,
"invocation": {"module_name": "expect"}, "module_stderr": "",
"module_stdout": "Traceback (most recent call last):\r\n  File
\"/tmp/ansible_V_modk/ansible_module_expect.py\", line 230, in <module>\r\n
main()\r\n  File \"/tmp/ansible_V_modk/ansible_module_expect.py\", line 199,
in main\r\n    events=events, cwd=chdir, echo=echo)\r\n  File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 225, in
runu\r\n    env=env, _spawn=spawnu, **kwargs)\r\n  File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 246, in
_run\r\n    index = child.expect(patterns)\r\n  File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1451, in
expect\r\n    timeout, searchwindowsize)\r\n  File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1466, in
expect_list\r\n    timeout, searchwindowsize)\r\n  File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1535, in
expect_loop\r\n    c = self.read_nonblocking(self.maxread, timeout)\r\n
File \"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 984, in
read_nonblocking\r\n    s = self._coerce_read_string(s)\r\n  File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1797, in
_coerce_read_string\r\n    return self._decoder.decode(s, final=False)\r\n
File \"/usr/lib64/python2.6/codecs.py\", line 296, in decode\r\n
(result, consumed) = self._buffer_decode(data, self.errors,
final)\r\nUnicodeDecodeError: 'utf8' codec can't decode byte 0xd2 in
position 1166: invalid continuation byte\r\n", "msg": "MODULE FAILURE",
"parsed": false}

# Fixed code
def main():
    module = AnsibleModule(
        argument_spec=dict(
            command=dict(required=True),
            chdir=dict(type="path"),
            creates=dict(type="path"),
            removes=dict(type="path"),
            responses=dict(type="dict", required=True),
            timeout=dict(type="int", default=30),
            echo=dict(type="bool", default=False),
        )
    )

    if not HAS_PEXPECT:
        module.fail_json(msg=missing_required_lib("pexpect"), exception=PEXPECT_IMP_ERR)

    chdir = module.params["chdir"]
    args = module.params["command"]
    creates = module.params["creates"]
    removes = module.params["removes"]
    responses = module.params["responses"]
    timeout = module.params["timeout"]
    echo = module.params["echo"]

    events = dict()
    for key, value in responses.items():
        if isinstance(value, list):
            response = response_closure(module, key, value)
        else:
            response = b"%s\n" % to_bytes(value).rstrip(b"\n")

        events[to_bytes(key)] = response

    if args.strip() == "":
        module.fail_json(rc=256, msg="no command given")

    if chdir:
        chdir = os.path.abspath(chdir)
        os.chdir(chdir)

    if creates:
        # do not run the command if the line contains creates=filename
        # and the filename already exists.  This allows idempotence
        # of command executions.
        if os.path.exists(creates):
            module.exit_json(
                cmd=args,
                stdout="skipped, since %s exists" % creates,
                changed=False,
                rc=0,
            )

    if removes:
        # do not run the command if the line contains removes=filename
        # and the filename does not exist.  This allows idempotence
        # of command executions.
        if not os.path.exists(removes):
            module.exit_json(
                cmd=args,
                stdout="skipped, since %s does not exist" % removes,
                changed=False,
                rc=0,
            )

    startd = datetime.datetime.now()

    try:
        try:
            # Prefer pexpect.run from pexpect>=4
            b_out, rc = pexpect.run(
                args,
                timeout=timeout,
                withexitstatus=True,
                events=events,
                cwd=chdir,
                echo=echo,
                encoding=None,
            )
        except TypeError:
            # Use pexpect._run in pexpect>=3.3,<4
            # pexpect.run doesn't support `echo`
            # pexpect.runu doesn't support encoding=None
            b_out, rc = pexpect._run(
                args,
                timeout=timeout,
                withexitstatus=True,
                events=events,
                extra_args=None,
                logfile=None,
                cwd=chdir,
                env=None,
                _spawn=pexpect.spawn,
                echo=echo,
            )

    except (TypeError, AttributeError) as e:
        # This should catch all insufficient versions of pexpect
        # We deem them insufficient for their lack of ability to specify
        # to not echo responses via the run/runu functions, which would
        # potentially leak sensitive information
        module.fail_json(
            msg="Insufficient version of pexpect installed "
            "(%s), this module requires pexpect>=3.3. "
            "Error was %s" % (pexpect.__version__, to_native(e))
        )
    except pexpect.ExceptionPexpect as e:
        module.fail_json(msg="%s" % to_native(e), exception=traceback.format_exc())

    endd = datetime.datetime.now()
    delta = endd - startd

    if b_out is None:
        b_out = b""

    result = dict(
        cmd=args,
        stdout=to_native(b_out).rstrip("\r\n"),
        rc=rc,
        start=str(startd),
        end=str(endd),
        delta=str(delta),
        changed=True,
    )

    if rc is None:
        module.fail_json(msg="command exceeded timeout", **result)
    elif rc != 0:
        module.fail_json(msg="non-zero return code", **result)

    module.exit_json(**result)