# Buggy code
def _thread_return(cls, minion_instance, opts, data):
    """
    This method should be used as a threading target, start the actual
    minion side execution.
    """
    # this seems awkward at first, but it's a workaround for Windows
    # multiprocessing communication.
    if not minion_instance:
        minion_instance = cls(opts)
    if opts["multiprocessing"]:
        fn_ = os.path.join(minion_instance.proc_dir, data["jid"])
        salt.utils.daemonize_if(opts)
        sdata = {"pid": os.getpid()}
        sdata.update(data)
        with salt.utils.fopen(fn_, "w+") as fp_:
            fp_.write(minion_instance.serial.dumps(sdata))
    ret = {"success": False}
    function_name = data["fun"]
    if function_name in minion_instance.functions:
        try:
            func = minion_instance.functions[data["fun"]]
            args, kwargs = parse_args_and_kwargs(func, data["arg"], data)
            sys.modules[func.__module__].__context__["retcode"] = 0
            return_data = func(*args, **kwargs)
            if isinstance(return_data, types.GeneratorType):
                ind = 0
                iret = {}
                for single in return_data:
                    if isinstance(single, dict) and isinstance(iret, list):
                        iret.update(single)
                    else:
                        if not iret:
                            iret = []
                        iret.append(single)
                    tag = tagify([data["jid"], "ret", opts["id"], ind])
                    minion_instance._fire_master({"return": single}, tag)
                    ind += 1
                ret["return"] = iret
            else:
                ret["return"] = return_data
            ret["retcode"] = sys.modules[func.__module__].__context__.get("retcode", 0)
            ret["success"] = True
        except CommandNotFoundError as exc:
            msg = "Command required for {0!r} not found".format(function_name)
            log.debug(msg, exc_info=True)
            ret["return"] = "{0}: {1}".format(msg, exc)
        except CommandExecutionError as exc:
            log.error(
                "A command in {0!r} had a problem: {1}".format(function_name, exc),
                exc_info=log.isEnabledFor(logging.DEBUG),
            )
            ret["return"] = "ERROR: {0}".format(exc)
        except SaltInvocationError as exc:
            log.error(
                "Problem executing {0!r}: {1}".format(function_name, exc),
                exc_info=log.isEnabledFor(logging.DEBUG),
            )
            ret["return"] = "ERROR executing {0!r}: {1}".format(function_name, exc)
        except TypeError as exc:
            trb = traceback.format_exc()
            aspec = salt.utils.get_function_argspec(
                minion_instance.functions[data["fun"]]
            )
            msg = (
                "TypeError encountered executing {0}: {1}. See "
                "debug log for more info.  Possibly a missing "
                "arguments issue:  {2}"
            ).format(function_name, exc, aspec)
            log.warning(msg, exc_info=log.isEnabledFor(logging.DEBUG))
            ret["return"] = msg
        except Exception:
            msg = "The minion function caused an exception"
            log.warning(msg, exc_info=log.isEnabledFor(logging.DEBUG))
            ret["return"] = "{0}: {1}".format(msg, traceback.format_exc())
    else:
        ret["return"] = "{0!r} is not available.".format(function_name)

    ret["jid"] = data["jid"]
    ret["fun"] = data["fun"]
    minion_instance._return_pub(ret)
    if data["ret"]:
        ret["id"] = opts["id"]
        for returner in set(data["ret"].split(",")):
            try:
                minion_instance.returners["{0}.returner".format(returner)](ret)
            except Exception as exc:
                log.error("The return failed for job {0} {1}".format(data["jid"], exc))


# Traceback (AttributeError)
[ERROR   ] Error in function _master_opts:
Traceback (most recent call last):
File "/usr/lib/pymodules/python2.7/salt/master.py", line 1444, in run_func
ret = getattr(self, func)(load)
File "/usr/lib/pymodules/python2.7/salt/master.py", line 891, in _master_opts
envs = self._file_envs()
File "/usr/lib/pymodules/python2.7/salt/fileserver/__init__.py", line 153, in envs
ret.update(self.servers[fstr]())
File "/usr/lib/pymodules/python2.7/salt/fileserver/hgfs.py", line 176, in envs
tags = repo.get_tags()
AttributeError: 'hgclient' object has no attribute 'get_tags'

# Fixed code
def _thread_return(cls, minion_instance, opts, data):
    """
    This method should be used as a threading target, start the actual
    minion side execution.
    """
    # this seems awkward at first, but it's a workaround for Windows
    # multiprocessing communication.
    if not minion_instance:
        minion_instance = cls(opts)
    if opts["multiprocessing"]:
        fn_ = os.path.join(minion_instance.proc_dir, data["jid"])
        salt.utils.daemonize_if(opts)
        sdata = {"pid": os.getpid()}
        sdata.update(data)
        with salt.utils.fopen(fn_, "w+") as fp_:
            fp_.write(minion_instance.serial.dumps(sdata))
    ret = {"success": False}
    function_name = data["fun"]
    if function_name in minion_instance.functions:
        try:
            func = minion_instance.functions[data["fun"]]
            args, kwargs = parse_args_and_kwargs(func, data["arg"], data)
            sys.modules[func.__module__].__context__["retcode"] = 0
            return_data = func(*args, **kwargs)
            if isinstance(return_data, types.GeneratorType):
                ind = 0
                iret = {}
                for single in return_data:
                    if isinstance(single, dict) and isinstance(iret, list):
                        iret.update(single)
                    else:
                        if not iret:
                            iret = []
                        iret.append(single)
                    tag = tagify([data["jid"], "prog", opts["id"], str(ind)], "job")
                    event_data = {"return": single}
                    minion_instance._fire_master(event_data, tag)
                    ind += 1
                ret["return"] = iret
            else:
                ret["return"] = return_data
            ret["retcode"] = sys.modules[func.__module__].__context__.get("retcode", 0)
            ret["success"] = True
        except CommandNotFoundError as exc:
            msg = "Command required for {0!r} not found".format(function_name)
            log.debug(msg, exc_info=True)
            ret["return"] = "{0}: {1}".format(msg, exc)
        except CommandExecutionError as exc:
            log.error(
                "A command in {0!r} had a problem: {1}".format(function_name, exc),
                exc_info=log.isEnabledFor(logging.DEBUG),
            )
            ret["return"] = "ERROR: {0}".format(exc)
        except SaltInvocationError as exc:
            log.error(
                "Problem executing {0!r}: {1}".format(function_name, exc),
                exc_info=log.isEnabledFor(logging.DEBUG),
            )
            ret["return"] = "ERROR executing {0!r}: {1}".format(function_name, exc)
        except TypeError as exc:
            trb = traceback.format_exc()
            aspec = salt.utils.get_function_argspec(
                minion_instance.functions[data["fun"]]
            )
            msg = (
                "TypeError encountered executing {0}: {1}. See "
                "debug log for more info.  Possibly a missing "
                "arguments issue:  {2}"
            ).format(function_name, exc, aspec)
            log.warning(msg, exc_info=log.isEnabledFor(logging.DEBUG))
            ret["return"] = msg
        except Exception:
            msg = "The minion function caused an exception"
            log.warning(msg, exc_info=log.isEnabledFor(logging.DEBUG))
            ret["return"] = "{0}: {1}".format(msg, traceback.format_exc())
    else:
        ret["return"] = "{0!r} is not available.".format(function_name)

    ret["jid"] = data["jid"]
    ret["fun"] = data["fun"]
    ret["fun_args"] = data["arg"]
    minion_instance._return_pub(ret)
    if data["ret"]:
        ret["id"] = opts["id"]
        for returner in set(data["ret"].split(",")):
            try:
                minion_instance.returners["{0}.returner".format(returner)](ret)
            except Exception as exc:
                log.error("The return failed for job {0} {1}".format(data["jid"], exc))