Exemplo n.º 1
0
    def _find_callables(cls, typed: _T):
        """Attempts to find callables nested in Lists, Tuples, or Dicts

        Args:
            typed: input type

        Returns:
            boolean if callables are found

        """
        out = False
        if hasattr(typed, "__args__"):
            if (_get_name_py_version(typed) == "List"
                    or _get_name_py_version(typed) == "Tuple"):
                # Possibly nested Callables
                if not isinstance(typed.__args__[0],
                                  _SpockVariadicGenericAlias):
                    out = cls._find_callables(typed.__args__[0])
                # Found callables
                elif isinstance(typed.__args__[0], _SpockVariadicGenericAlias):
                    out = True
            elif _get_name_py_version(typed) == "Dict":
                key_type, value_type = typed.__args__
                if not isinstance(value_type, _SpockVariadicGenericAlias):
                    out = cls._find_callables(value_type)
                elif isinstance(value_type, _SpockVariadicGenericAlias):
                    out = True
            else:
                raise TypeError(
                    f"Unexpected type of `{str(typed)}` when attempting to handle GenericAlias types"
                )
        return out
Exemplo n.º 2
0
    def handle_attribute_from_config(self, attr_space: AttributeSpace,
                                     builder_space: BuilderSpace):
        """Handles setting a simple attribute when it is a spock class type

        Args:
            attr_space: holds information about a single attribute that is mapped to a ConfigSpace
            builder_space: named_tuple containing the arguments and spock_space

        Returns:
        """
        typed = attr_space.attribute.metadata["type"]
        out = None
        # Handle List Types
        if (_get_name_py_version(typed) == "List"
                or _get_name_py_version(typed) == "Tuple"):
            out = []
            for v in builder_space.arguments[attr_space.config_space.name][
                    attr_space.attribute.name]:
                out.append(_recurse_callables(v, _str_2_callable))
        # Handle Dict Types
        elif _get_name_py_version(typed) == "Dict":
            out = {}
            for k, v in builder_space.arguments[attr_space.config_space.name][
                    attr_space.attribute.name].items():
                out.update({k: _recurse_callables(v, _str_2_callable)})
        attr_space.field = out
Exemplo n.º 3
0
def _type_katra(typed, default=None, optional=False):
    """Private interface to create a simple typed katra

    A 'katra' is the basic functional unit of `spock`. It defines a parameter using attrs as the backend, type checks
    both simple types and subscripted GenericAlias types (e.g. lists and tuples), handles setting default parameters,
    and deals with parameter optionality

    Handles: bool, string, float, int, List, and Tuple

    Args:
        typed: the type of the parameter to define
        default: the default value to assign if given
        optional: whether to make the parameter optional or not (thus allowing None)

    Returns:
        x: Attribute from attrs

    """
    # Grab the name first based on if it is a base type or GenericAlias
    if isinstance(typed, type):
        name = typed.__name__
    elif isinstance(typed, _SpockGenericAlias):
        name = _get_name_py_version(typed=typed)
    else:
        raise TypeError("Encountered an unexpected type in _type_katra")
    special_key = None
    # Default booleans to false and optional due to the nature of a boolean
    if isinstance(typed, type) and name == "bool":
        optional = True
        if default is not True:
            default = False
    # For the save path type we need to swap the type back to it's base class (str)
    elif isinstance(typed, type) and name == "SavePath":
        optional = True
        special_key = name
        typed = str
    if default is not None:
        # if a default is provided, that takes precedence
        x = attr.ib(
            validator=attr.validators.instance_of(typed),
            default=default,
            type=typed,
            metadata={"base": name, "special_key": special_key},
        )
    elif optional:
        x = attr.ib(
            validator=attr.validators.optional(attr.validators.instance_of(typed)),
            default=default,
            type=typed,
            metadata={"optional": True, "base": name, "special_key": special_key},
        )
    else:
        x = attr.ib(
            validator=attr.validators.instance_of(typed),
            type=typed,
            metadata={"base": name, "special_key": special_key},
        )
    return x
Exemplo n.º 4
0
def _extract_base_type(typed):
    """Extracts the name of the type from a _GenericAlias

    Assumes that the derived types are only of length 1 as the __args__ are [0] recursed... this is not true for
    tuples

    Args:
        typed: the type of the parameter

    Returns:
        name of type
    """
    if hasattr(typed, "__args__") and not isinstance(typed, _SpockVariadicGenericAlias):
        name = _get_name_py_version(typed=typed)
        bracket_val = f"{name}[{_extract_base_type(typed.__args__[0])}]"
        return bracket_val
    else:
        bracket_value = _get_name_py_version(typed=typed)
    return bracket_value
Exemplo n.º 5
0
def _callable_katra(typed, default=None, optional=False):
    """Private interface to create a Callable katra

    Here we handle the callable type katra that allows us to force a callable check on the value provided

    A 'katra' is the basic functional unit of `spock`. It defines a parameter using attrs as the backend, type checks
    both simple types and subscripted GenericAlias types (e.g. lists and tuples), handles setting default parameters,
    and deals with parameter optionality

    Handles: bool, string, float, int, List, and Tuple

    Args:
        typed: the type of the parameter to define
        default: the default value to assign if given
        optional: whether to make the parameter optional or not (thus allowing None)

    Returns:
        x: Attribute from attrs

    """
    if default is not None:
        # if a default is provided, that takes precedence
        x = attr.ib(
            validator=attr.validators.is_callable(),
            default=default,
            type=typed,
            metadata={"base": _get_name_py_version(typed)},
        )
    elif optional:
        x = attr.ib(
            validator=attr.validators.optional(attr.validators.is_callable()),
            default=default,
            type=typed,
            metadata={"optional": True, "base": _get_name_py_version(typed)},
        )
    else:
        x = attr.ib(
            validator=attr.validators.is_callable(),
            type=typed,
            metadata={"base": _get_name_py_version(typed)},
        )
    return x
Exemplo n.º 6
0
def _recursive_generic_validator(typed):
    """Recursively assembles the validators for nested generic types

    Walks through the nested type structure and determines whether to recurse all the way to a base type. Once it
    hits the base type it bubbles up the correct validator that is nested within the upper validator

    Args:
        typed: input type

    Returns:
        return_type: recursively built deep_iterable validators

    """
    if hasattr(typed, "__args__") and not isinstance(typed, _SpockVariadicGenericAlias):
        # Iterate through since there might be multiple types?
        # Handle List and Tuple types
        if (
            _get_name_py_version(typed) == "List"
            or _get_name_py_version(typed) == "Tuple"
        ):
            # If there are more __args__ then we still need to recurse as it is still a GenericAlias
            if len(typed.__args__) > 1:
                return_type = attr.validators.deep_iterable(
                    member_validator=_recursive_generic_validator(typed.__args__),
                    iterable_validator=attr.validators.instance_of(typed.__origin__),
                )
            else:
                return_type = attr.validators.deep_iterable(
                    member_validator=_recursive_generic_validator(typed.__args__[0]),
                    iterable_validator=attr.validators.instance_of(typed.__origin__),
                )
            return return_type
        # Handle Dict types
        elif _get_name_py_version(typed) == "Dict":
            key_type, value_type = typed.__args__
            if key_type is not str:
                raise TypeError(
                    f"Unexpected key type of `{str(key_type.__name__)}` when attempting to handle "
                    f"GenericAlias type of Dict -- currently Spock only supports str as keys due "
                    f"to maintaining support for valid TOML and JSON files"
                )
            if hasattr(value_type, "__args__") and not isinstance(
                typed, _SpockVariadicGenericAlias
            ):
                return_type = attr.validators.deep_mapping(
                    value_validator=_recursive_generic_validator(value_type),
                    key_validator=attr.validators.instance_of(key_type),
                )
            else:
                return_type = attr.validators.deep_mapping(
                    value_validator=attr.validators.instance_of(value_type),
                    key_validator=attr.validators.instance_of(key_type),
                )
            return return_type
        else:
            raise TypeError(
                f"Unexpected type of `{str(typed)}` when attempting to handle GenericAlias types"
            )
    else:
        # If no more __args__ then we are to the base type and need to bubble up the type
        # But we need to check against base types and enums
        if isinstance(typed, EnumMeta):
            base_type, allowed = _check_enum_props(typed)
            return_type = attr.validators.and_(
                attr.validators.instance_of(base_type), attr.validators.in_(allowed)
            )
        else:
            return_type = attr.validators.instance_of(typed)
    return return_type