Ejemplo n.º 1
0
def is_optional_type(t: Any, test_type: Optional[type] = None) -> bool:
    """Check if `t` is optional type (Union[None, ...]).

    And optionally check if T is of `test_type`

    >>> is_optional_type(Optional[int])
    True
    >>> is_optional_type(Union[None, int])
    True
    >>> is_optional_type(Union[int, str, None])
    True
    >>> is_optional_type(Optional[int], int)
    True
    >>> is_optional_type(Optional[int], str)
    False
    >>> is_optional_type(Optional[State], int)
    False
    >>> is_optional_type(Optional[State], State)
    True
    """
    if get_origin(t) in union_types and None.__class__ in get_args(t):
        for arg in get_args(t):
            if arg is None.__class__:
                continue

            return not test_type or is_of_type(arg, test_type)
    return False
Ejemplo n.º 2
0
def is_of_type(t: Any, test_type: Any) -> bool:
    """Check if annotation type is valid for type.

    >>> is_of_type(list, list)
    True
    >>> is_of_type(List[int], List[int])
    True
    >>> is_of_type(strEnum, str)
    True
    >>> is_of_type(strEnum, Enum)
    True
    >>> is_of_type(int, str)
    False
    """

    if is_union_type(test_type):
        return any(get_origin(t) is get_origin(arg) for arg in get_args(test_type))

    if (
        get_origin(t)
        and get_origin(test_type)
        and get_origin(t) is get_origin(test_type)
        and get_args(t) == get_args(test_type)
    ):
        return True

    if test_type is t:
        # Test type is a typing type instance and matches
        return True

    # Workaround for the fact that you can't call issubclass on typing types
    try:
        return issubclass(t, test_type)
    except TypeError:
        return False
Ejemplo n.º 3
0
def is_list_type(t: Any, test_type: Optional[type] = None) -> bool:
    """Check if `t` is list type.

    And optionally check if the list items are of `test_type`

    >>> is_list_type(List[int])
    True
    >>> is_list_type(Optional[List[int]])
    True
    >>> is_list_type(Optional[List[int]], int)
    True
    >>> is_list_type(Optional[List[int]], str)
    False
    >>> is_list_type(Optional[int])
    False
    >>> is_list_type(List[Tuple[int, int]])
    True
    >>> is_list_type(List[Tuple[int, int]], int)
    False
    >>> is_list_type(List[Tuple[int, int]], Tuple[int, int])
    True
    >>> is_list_type(List[strEnum], Enum)
    True
    >>> is_list_type(int)
    False
    >>> is_list_type(Literal[1,2,3])
    False
    >>> is_list_type(List[Union[str, int]])
    True
    >>> is_list_type(List[Union[str, int]], Union[str, int])
    False
    >>> is_list_type(List[Union[str, int]], str)
    True
    >>> is_list_type(List[Union[str, int]], int)
    False
    >>> is_list_type(List[Union[str, int]], Union[int, int])
    False
    """
    if get_origin(t):
        if is_optional_type(t) or is_union_type(t):
            for arg in get_args(t):
                if is_list_type(arg, test_type):
                    return True
        elif get_origin(t) == Literal:  # type:ignore
            return False  # Literal cannot contain lists see pep 586
        elif issubclass(get_origin(t), list):  # type: ignore
            if test_type and get_args(t):
                first_arg = get_args(t)[0]
                # To support a list with union of multiple product blocks.
                if is_union_type(first_arg) and get_args(first_arg) and not is_union_type(test_type):
                    first_arg = get_args(first_arg)[0]
                return is_of_type(first_arg, test_type)
            else:
                return True

    return False
Ejemplo n.º 4
0
def is_union_type(t: Any, test_type: Optional[type] = None) -> bool:
    """Check if `t` is union type (Union[Type, AnotherType]).

    Optionally check if T is of `test_type` We cannot check for literal Nones.

    >>> is_union_type(Union[int, str])
    True
    >>> is_union_type(Union[int, str], str)
    True
    >>> is_union_type(Union[int, str], Union[int, str])
    True
    >>> is_union_type(Union[int, None])
    True
    >>> is_union_type(int)
    False
    """
    if get_origin(t) not in union_types:
        return False
    if not test_type:
        return True

    if is_of_type(t, test_type):
        return True
    for arg in get_args(t):
        result = is_of_type(arg, test_type)
        if result:
            return result
    return False
Ejemplo n.º 5
0
def get_possible_product_block_types(list_field_type: Any) -> dict:
    possible_product_block_types = {}
    if is_union_type(list_field_type):
        for list_item_field_type in get_args(list_field_type):
            if (
                not isinstance(None, list_item_field_type)
                and list_item_field_type.name not in possible_product_block_types
            ):
                possible_product_block_types[list_item_field_type.name] = list_item_field_type
    else:
        possible_product_block_types = {list_field_type.name: list_field_type}
    return possible_product_block_types
Ejemplo n.º 6
0
def test_get_args(input_value, output_value):
    if input_value is None:
        pytest.skip('Skipping undefined hint for this python version')
    assert get_args(input_value) == output_value
Ejemplo n.º 7
0
    def _analyze_arg_type(self, arg_type: Optional[type]):
        if arg_type is None:
            return

        origin = get_origin(arg_type)
        if origin:
            shape = None
            dtype = None

            if origin is Union:
                allow_types = []

                for arg in get_args(arg_type):
                    if arg is not None:
                        allow_types.append(arg)

                self.shape = ArgTypeShape.UNION
                self.outer_type = [self._get_arg_type(t) for t in allow_types]
                return

            if inspect.isclass(origin):
                if issubclass(origin, List):
                    shape = ArgTypeShape.LIST
                    args = get_args(arg_type)
                    if args and len(args) == 1:
                        vt = args[0]
                        if not inspect.isclass(vt):
                            vt = None
                        dtype = self._get_arg_type(vt)
                elif issubclass(origin, Mapping):
                    shape = ArgTypeShape.DICT
                    args = get_args(arg_type)
                    if args and len(args) == 2:
                        kt = args[0]
                        vt = args[1]
                        dtype = (self._get_arg_type(kt),
                                 self._get_arg_type(vt))
                    else:
                        dtype = (None, None)
                elif issubclass(origin, Set):
                    shape = ArgTypeShape.SET
                    args = get_args(arg_type)
                    if args and len(args) == 1:
                        vt = args[0]
                        if not inspect.isclass(vt):
                            vt = None
                        dtype = self._get_arg_type(vt)
            if shape is None:
                raise AssertionError(
                    f'Unsupported generic argument type: {arg_type}')
            self.shape = shape
            self.outer_type = dtype
            return

        if isinstance(arg_type, str):
            if hasattr(builtins, arg_type):
                arg_type = getattr(builtins, arg_type)
                self.outer_type = arg_type

        if not inspect.isclass(arg_type):
            raise AssertionError(
                f'Non-generic argument type must be a class, but got: {arg_type}'
            )
        if issubclass(arg_type, List):
            self.shape = ArgTypeShape.LIST
            self.outer_type = None
        elif issubclass(arg_type, Mapping):
            self.shape = ArgTypeShape.DICT
            self.outer_type = (None, None)
        elif issubclass(arg_type, Set):
            self.shape = ArgTypeShape.SET
            self.outer_type = None
Ejemplo n.º 8
0
def test_get_args(input_value, output_value):
    assert get_args(input_value) == output_value
Ejemplo n.º 9
0
def _build_arguments(func: Union[StepFunc, InputStepFunc],
                     state: State) -> List:
    """Build actual arguments based on step function signature and state.

    What the step function requests in its function signature it what this function retrieves from the state or DB.
    Domain models are retrieved from the DB (after `subscription_id` lookup in the state). Everything else is
    retrieved from the state.

    For domain models only ``Optional`` and ``List`` are supported as container types. Union, Dict and others are not supported

    Args:
        func: step function to inspect for requested arguments
        state: workflow state

    Returns:
        List of actual positional arguments.

    Raises:
         KeyError: if requested argument is not in the state, or cannot be reconstructed as an initial domain model.

    """
    sig = inspect.signature(func)
    arguments: List[Any] = []
    if sig.parameters:
        for name, param in sig.parameters.items():
            # Ignore dynamic arguments. Mostly need to deal with `const`
            if param.kind in (inspect.Parameter.VAR_POSITIONAL,
                              inspect.Parameter.VAR_KEYWORD):
                logger.warning(
                    "*args and **kwargs are not supported as step params")
                continue

            # If we find an argument named "state" we use the whole state as argument to
            # This is mainly to be backward compatible with code that needs the whole state...
            # TODO: Remove this construction
            if name == "state":
                arguments.append(state)
                continue

            # Workaround for the fact that you can't call issubclass on typing types
            try:
                is_subscription_model_type = issubclass(
                    param.annotation, SubscriptionModel)
            except Exception:
                is_subscription_model_type = False

            if is_subscription_model_type:
                subscription_id = _get_sub_id(state.get(name))
                if subscription_id:
                    sub_mod = param.annotation.from_subscription(
                        subscription_id)
                    arguments.append(sub_mod)
                else:
                    logger.error("Could not find key in state.",
                                 key=name,
                                 state=state)
                    raise KeyError(f"Could not find key '{name}' in state.")
            elif is_list_type(param.annotation, SubscriptionModel):
                subscription_ids = map(_get_sub_id, state.get(name, []))
                subscriptions = [
                    # Actual type is first argument from list type
                    get_args(param.annotation)
                    [0].from_subscription(subscription_id)
                    for subscription_id in subscription_ids
                ]
                arguments.append(subscriptions)
            elif is_optional_type(param.annotation, SubscriptionModel):
                subscription_id = _get_sub_id(state.get(name))
                if subscription_id:
                    # Actual type is first argument from optional type
                    sub_mod = get_args(
                        param.annotation)[0].from_subscription(subscription_id)
                    arguments.append(sub_mod)
                else:
                    arguments.append(None)
            elif param.default is not inspect.Parameter.empty:
                arguments.append(state.get(name, param.default))
            else:
                try:
                    arguments.append(state[name])
                except KeyError as key_error:
                    logger.error("Could not find key in state.",
                                 key=name,
                                 state=state)
                    raise KeyError(
                        f"Could not find key '{name}' in state. for function {func.__module__}.{func.__qualname__}"
                    ) from key_error
    return arguments