# Buggy code
def _validate_opts(opts):
    """
    Check that all of the types of values passed into the config are
    of the right types
    """

    def format_multi_opt(valid_type):
        try:
            num_types = len(valid_type)
        except TypeError:
            # Bare type name won't have a length, return the name of the type
            # passed.
            return valid_type.__name__
        else:
            if num_types == 1:
                return valid_type.__name__
            elif num_types > 1:
                ret = ", ".join(x.__name__ for x in valid_type[:-1])
                ret += " or " + valid_type[-1].__name__

    errors = []

    err = (
        "Key '{0}' with value {1} has an invalid type of {2}, a {3} is "
        "required for this value"
    )
    for key, val in six.iteritems(opts):
        if key in VALID_OPTS:
            if isinstance(val, VALID_OPTS[key]):
                continue

            if hasattr(VALID_OPTS[key], "__call__"):
                try:
                    VALID_OPTS[key](val)
                    if isinstance(val, (list, dict)):
                        # We'll only get here if VALID_OPTS[key] is str or
                        # bool, and the passed value is a list/dict. Attempting
                        # to run int() or float() on a list/dict will raise an
                        # exception, but running str() or bool() on it will
                        # pass despite not being the correct type.
                        errors.append(
                            err.format(
                                key, val, type(val).__name__, VALID_OPTS[key].__name__
                            )
                        )
                except (TypeError, ValueError):
                    errors.append(
                        err.format(
                            key, val, type(val).__name__, VALID_OPTS[key].__name__
                        )
                    )
                continue

            errors.append(
                err.format(
                    key,
                    val,
                    type(val).__name__,
                    format_multi_opt(VALID_OPTS[key].__name__),
                )
            )

    # RAET on Windows uses 'win32file.CreateMailslot()' for IPC. Due to this,
    # sock_dirs must start with '\\.\mailslot\' and not contain any colons.
    # We don't expect the user to know this, so we will fix up their path for
    # them if it isn't compliant.
    if (
        salt.utils.is_windows()
        and opts.get("transport") == "raet"
        and "sock_dir" in opts
        and not opts["sock_dir"].startswith("\\\\.\\mailslot\\")
    ):
        opts["sock_dir"] = "\\\\.\\mailslot\\" + opts["sock_dir"].replace(":", "")

    for error in errors:
        log.warning(error)
    if errors:
        return False
    return True


# Traceback (ParserError)
li1105-12 ~ master # salt jmoney-master state.show_top
jmoney-master:
The minion function caused an exception: Traceback (most recent call last):
File "/root/salt/salt/minion.py", line 1173, in _thread_return
return_data = func(*args, **kwargs)
File "/root/salt/salt/modules/state.py", line 1045, in show_top
top_ = st_.get_top()
File "/root/salt/salt/state.py", line 2436, in get_top
tops = self.get_tops()
File "/root/salt/salt/state.py", line 2313, in get_tops
saltenv=saltenv
File "/root/salt/salt/template.py", line 79, in compile_template
ret = render(input_data, saltenv, sls, **render_kwargs)
File "/root/salt/salt/renderers/yaml.py", line 50, in render
data = load(yaml_data, Loader=get_yaml_loader(argline))
File "/usr/lib/python2.7/dist-packages/yaml/__init__.py", line 71, in load
return loader.get_single_data()
File "/usr/lib/python2.7/dist-packages/yaml/constructor.py", line 37, in get_single_data
node = self.get_single_node()
File "/usr/lib/python2.7/dist-packages/yaml/composer.py", line 36, in get_single_node
document = self.compose_document()
File "/usr/lib/python2.7/dist-packages/yaml/composer.py", line 55, in compose_document
node = self.compose_node(None, None)
File "/usr/lib/python2.7/dist-packages/yaml/composer.py", line 84, in compose_node
node = self.compose_mapping_node(anchor)
File "/usr/lib/python2.7/dist-packages/yaml/composer.py", line 127, in compose_mapping_node
while not self.check_event(MappingEndEvent):
File "/usr/lib/python2.7/dist-packages/yaml/parser.py", line 98, in check_event
self.current_event = self.state()
File "/usr/lib/python2.7/dist-packages/yaml/parser.py", line 439, in parse_block_mapping_key
"expected <block end>, but found %r" % token.id, token.start_mark)
ParserError: while parsing a block mapping
in "<unicode string>", line 1, column 1:
base:
^
expected <block end>, but found '<block sequence start>'
in "<unicode string>", line 3, column 5:
- state1
^

# Fixed code
def _validate_opts(opts):
    """
    Check that all of the types of values passed into the config are
    of the right types
    """

    def format_multi_opt(valid_type):
        try:
            num_types = len(valid_type)
        except TypeError:
            # Bare type name won't have a length, return the name of the type
            # passed.
            return valid_type.__name__
        else:

            def get_types(types, type_tuple):
                for item in type_tuple:
                    if isinstance(item, tuple):
                        get_types(types, item)
                    else:
                        try:
                            types.append(item.__name__)
                        except AttributeError:
                            log.warning(
                                "Unable to interpret type %s while validating "
                                "configuration",
                                item,
                            )

            types = []
            get_types(types, valid_type)

            ret = ", ".join(types[:-1])
            ret += " or " + types[-1]
            return ret

    errors = []

    err = (
        "Key '{0}' with value {1} has an invalid type of {2}, a {3} is "
        "required for this value"
    )
    for key, val in six.iteritems(opts):
        if key in VALID_OPTS:
            if isinstance(val, VALID_OPTS[key]):
                continue

            if hasattr(VALID_OPTS[key], "__call__"):
                try:
                    VALID_OPTS[key](val)
                    if isinstance(val, (list, dict)):
                        # We'll only get here if VALID_OPTS[key] is str or
                        # bool, and the passed value is a list/dict. Attempting
                        # to run int() or float() on a list/dict will raise an
                        # exception, but running str() or bool() on it will
                        # pass despite not being the correct type.
                        errors.append(
                            err.format(
                                key, val, type(val).__name__, VALID_OPTS[key].__name__
                            )
                        )
                except (TypeError, ValueError):
                    errors.append(
                        err.format(
                            key, val, type(val).__name__, VALID_OPTS[key].__name__
                        )
                    )
                continue

            errors.append(
                err.format(
                    key, val, type(val).__name__, format_multi_opt(VALID_OPTS[key])
                )
            )

    # RAET on Windows uses 'win32file.CreateMailslot()' for IPC. Due to this,
    # sock_dirs must start with '\\.\mailslot\' and not contain any colons.
    # We don't expect the user to know this, so we will fix up their path for
    # them if it isn't compliant.
    if (
        salt.utils.is_windows()
        and opts.get("transport") == "raet"
        and "sock_dir" in opts
        and not opts["sock_dir"].startswith("\\\\.\\mailslot\\")
    ):
        opts["sock_dir"] = "\\\\.\\mailslot\\" + opts["sock_dir"].replace(":", "")

    for error in errors:
        log.warning(error)
    if errors:
        return False
    return True