Beispiel #1
0
def test_generic_utils():
    assert get_origin(list) is None
    assert get_origin(typing.Union) is None

    assert get_args(typing.List) == tuple()
    assert get_origin(typing.List) == list

    assert get_args(typing.List[int]) == (int, )
    assert get_origin(typing.List[int]) == list
    assert get_args(typing.List[str]) != (int, )

    assert get_origin(typing.Union[int, str]) == typing.Union
    assert get_args(typing.Union[int, str]) == (int, str)

    fun = typing.Callable[[str, int], int]
    assert get_origin(fun) == collections.abc.Callable
    assert get_args(fun) == ([str, int], int)

    PayloadType = typing.TypeVar("PayloadType")

    class TypeA(typing.Generic[PayloadType]):
        def __init__(self, payload: PayloadType):
            self.payload = payload

    assert get_origin(TypeA[int]) == TypeA
    assert get_args(TypeA[int]) == (int, )
Beispiel #2
0
    def build_variable_value_tuple(
        self, variable_values: Tuple[hints.VariableValue,
                                     ...]) -> VariableValueTuple:
        """Prepares and validates a raw tuple of variable values to be passed into
        `compute_residual_vector` or `compute_residual_jacobians`.

        Slightly sketchy: checks the type hinting on `compute_residual_vector` and if
        the user expects a named tuple, we wrap the input accordingly. Otherwise, we
        just cast and return the input."""

        assert isinstance(variable_values, tuple)

        output: VariableValueTuple

        try:
            value_type: Type[VariableValueTuple] = get_type_hints(
                self.compute_residual_vector)["variable_values"]
        except KeyError as e:
            raise NotImplementedError(
                f"Missing type hints for {type(self).__name__}.compute_residual_vector"
            ) from e

        # Function should be hinted with a tuple of some kind, but not `tuple` itself
        assert issubtype(value_type, tuple), value_type is not tuple

        # Heuristic: evaluates to `True` for NamedTuple types but `False` for
        # `Tuple[...]` types. Note that standard superclass checking approaches don't
        # work for NamedTuple types.
        if type(value_type) is type:
            # Hint is `NamedTuple`
            tuple_content_types = tuple(get_type_hints(value_type).values())
            output = value_type(*variable_values)
        else:
            # Hint is `typing.Tuple` annotation
            tuple_content_types = get_args(value_type)
            output = cast(VariableValueTuple, variable_values)

        # Handle Ellipsis in type hints, eg `Tuple[SomeType, ...]`
        if len(tuple_content_types
               ) == 2 and tuple_content_types[1] is Ellipsis:
            tuple_content_types = tuple_content_types[0:1] * len(
                variable_values)

        # Validate expected and received types
        assert len(variable_values) == len(tuple_content_types)
        for i, (value, expected_type) in enumerate(
                zip(variable_values, tuple_content_types)):
            assert isinstance(value, expected_type), (
                f"Variable value type hint inconsistency: expected {expected_type} at, "
                f"position {i} but got {type(value)}.")

        return output
Beispiel #3
0
def _contains_unbound_typevar(t: Type) -> bool:
    """Recursively check if `t` or any types contained by `t` is a `TypeVar`.

    Examples where we return `True`: `T`, `Optional[T]`, `Tuple[Optional[T], ...]`, ...
    Examples where we return `False`: `int`, `Optional[str]`, ...

    :param t: Type to evaluate.
    :return: `True` if the input type contains an unbound `TypeVar`, `False` otherwise.
    """

    # Check self
    if isinstance(t, TypeVar):
        return True

    # Check children
    for arg in get_args(t):
        if _contains_unbound_typevar(arg):
            return True

    return False
Beispiel #4
0
def _validate_run_fn_return_type(node: SchemaNode, return_type: Type,
                                 is_training: bool) -> None:
    if return_type == inspect.Parameter.empty:
        raise GraphSchemaValidationException(
            f"Your model uses a component '{node.uses.__name__}' whose "
            f"method '{node.fn}' does not have a type annotation for "
            f"its return value. Type annotations are required for all "
            f"components to validate your model's structure."
            f"See {DOCS_URL_GRAPH_COMPONENTS} for more information.")

    # TODO: Handle forward references here
    if typing_utils.issubtype(return_type, list):
        return_type = typing_utils.get_args(return_type)[0]

    if is_training and not isinstance(return_type, Fingerprintable):
        raise GraphSchemaValidationException(
            f"Your model uses a component '{node.uses.__name__}' whose method "
            f"'{node.fn}' does not return a fingerprintable "
            f"output. This is required for proper caching between model trainings. "
            f"Please make sure you're using a return type which implements the "
            f"'{Fingerprintable.__name__}' protocol.")
Beispiel #5
0
def is_optional(type_):
    """
    ``Optional[X]`` is equivalent to ``Union[X, None]``.
    """
    return get_origin(type_) is Union and get_args(type_)[1] is type(None)