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, )
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
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
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.")
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)