Пример #1
0
def issubclass(sub: Type, super: Union[Type, Tuple[Type, ...]]) -> bool:
    """
    Alternative issubclass implementation that interpretes instances of NewType for the first argument as their super type.
    """
    if typing_inspect.is_new_type(sub):
        return issubclass(sub.__supertype__, super)
    return builtins.issubclass(sub, super)
Пример #2
0
def _get_object_path_function(obj: types.FunctionType) -> Optional[str]:
    if typing_inspect.is_new_type(obj):
        return None

    return '.'.join((
        obj.__module__,
        obj.__qualname__,
    ))
Пример #3
0
def _repr(val: t.Any) -> str:

    assert val is not None

    if types.is_none_type(val):
        return 'NoneType'
    elif ti.is_literal_type(val):
        return str(val)
    elif ti.is_new_type(val):
        nested_type = val.__supertype__
        return f'{_qualified_name(val)}[{get_repr(nested_type)}]'
    elif ti.is_typevar(val):
        tv_constraints = ti.get_constraints(val)
        tv_bound = ti.get_bound(val)
        if tv_constraints:
            constraints_repr = (get_repr(tt) for tt in tv_constraints)
            return f'typing.TypeVar(?, {", ".join(constraints_repr)})'
        elif tv_bound:
            return get_repr(tv_bound)
        else:
            return 'typing.Any'
    elif ti.is_optional_type(val):
        optional_args = ti.get_args(val, True)[:-1]
        nested_union = len(optional_args) > 1
        optional_reprs = (get_repr(tt) for tt in optional_args)
        if nested_union:
            return f'typing.Optional[typing.Union[{", ".join(optional_reprs)}]]'
        else:
            return f'typing.Optional[{", ".join(optional_reprs)}]'
    elif ti.is_union_type(val):
        union_reprs = (get_repr(tt) for tt in ti.get_args(val, True))
        return f'typing.Union[{", ".join(union_reprs)}]'
    elif ti.is_generic_type(val):
        attr_name = val._name
        generic_reprs = (get_repr(tt) for tt in ti.get_args(val, evaluate=True))
        return f'typing.{attr_name}[{", ".join(generic_reprs)}]'
    else:
        val_name = _qualified_name(val)
        maybe_td_entries = getattr(val, '__annotations__', {}).copy()
        if maybe_td_entries:
            # we are dealing with typed dict
            # that's quite lovely
            td_keys = sorted(maybe_td_entries.keys())
            internal_members_repr = ', '.join(
                '{key}: {type}'.format(key=k, type=get_repr(maybe_td_entries.get(k)))
                for k in td_keys
            )
            return f'{val_name}{{{internal_members_repr}}}'
        elif 'TypedDict' == getattr(val, '__name__', ''):
            return 'typing_extensions.TypedDict'
        else:
            return val_name
Пример #4
0
def _maybe_node_for_newtype(
    typ: Union[NewType, Any], overrides: OverridesT, memo: MemoType,
    forward_refs: ForwardRefs
) -> Tuple[Optional[schema.nodes.SchemaNode], MemoType, ForwardRefs]:
    """ newtypes do not change the underlying runtime data type that is used in
    calls like isinstance(), therefore it's just enough for us to find
    a schema node of the underlying type
    """
    rv = None
    if insp.is_new_type(typ):
        return decide_node_type(typ.__supertype__, overrides, memo,
                                forward_refs)
    return rv, memo, forward_refs
def _get_object_name_from_function(obj: types.FunctionType) -> str:
    if typing_inspect.is_new_type(obj):
        # NewType is a function with __supertype__ attribute assigned to it.
        supertype = get_object_name(obj.__supertype__)
        return f'NewType {obj.__name__}({supertype})'

    # We only print the module in which function is. We do not print the whole
    # module path.
    module_name = obj.__module__.split('.')[-1]

    return '{}.{}'.format(
        module_name,
        obj.__qualname__,
    )
Пример #6
0
    def get(cls, type_or_hint, *, is_argument: bool = False) -> "TypeChecker":
        # This ensures the validity of the type passed (see typing documentation for info)
        type_or_hint = is_valid_type(type_or_hint, "Invalid type.",
                                     is_argument)

        if type_or_hint is Any:
            return AnyTypeChecker()

        if is_type(type_or_hint):
            return TypeTypeChecker.make(type_or_hint, is_argument)

        if is_literal_type(type_or_hint):
            return LiteralTypeChecker.make(type_or_hint, is_argument)

        if is_generic_type(type_or_hint):
            origin = get_origin(type_or_hint)
            if issubclass(origin, MappingCol):
                return MappingTypeChecker.make(type_or_hint, is_argument)

            if issubclass(origin, Collection):
                return CollectionTypeChecker.make(type_or_hint, is_argument)

            # CONSIDER: how to cater for exhaustible generators?
            if issubclass(origin, Iterable):
                raise NotImplementedError(
                    "No type-checker is setup for iterables that exhaust.")

            return GenericTypeChecker.make(type_or_hint, is_argument)

        if is_tuple_type(type_or_hint):
            return TupleTypeChecker.make(type_or_hint, is_argument)

        if is_callable_type(type_or_hint):
            return CallableTypeChecker.make(type_or_hint, is_argument)

        if isclass(type_or_hint):
            if is_typed_dict(type_or_hint):
                return TypedDictChecker.make(type_or_hint, is_argument)
            return ConcreteTypeChecker.make(type_or_hint, is_argument)

        if is_union_type(type_or_hint):
            return UnionTypeChecker.make(type_or_hint, is_argument)

        if is_typevar(type_or_hint):
            bound_type = get_bound(type_or_hint)
            if bound_type:
                return cls.get(bound_type)
            constraints = get_constraints(type_or_hint)
            if constraints:
                union_type_checkers = tuple(
                    cls.get(type_) for type_ in constraints)
                return UnionTypeChecker(Union.__getitem__(constraints),
                                        union_type_checkers)
            else:
                return AnyTypeChecker()

        if is_new_type(type_or_hint):
            super_type = getattr(type_or_hint, "__supertype__", None)
            if super_type is None:
                raise TypeError(
                    f"No supertype for NewType: {type_or_hint}. This is not allowed."
                )
            return cls.get(super_type)

        if is_forward_ref(type_or_hint):
            return ForwardTypeChecker.make(type_or_hint,
                                           is_argument=is_argument)

        if is_classvar(type_or_hint):
            var_type = get_args(type_or_hint, evaluate=True)[0]
            return cls.get(var_type)

        raise NotImplementedError(
            f"No {TypeChecker.__qualname__} is available for type or hint: '{type_or_hint}'"
        )
Пример #7
0
def _analyze_http_code(
    operation: oas.OASOperation,
    rt_http_code: t.Optional[t.Type[t.Any]],
) -> t.Set[exceptions.Error]:

    if rt_http_code is None:
        # if there's no http_code in return Response
        # this is permitted only if there's single response defined in
        # OAS responses. User needs to set it otherwise how can we tell if
        # everything is correct
        if len(operation.responses) != 1:
            logger.opt(lazy=True).error(
                'Operation {id} handler skips return.http_code but it is impossible '
                ' with {count_of_ops} responses due to ambiguity.',
                id=lambda: operation.id,
                count_of_ops=lambda: len(operation.responses),
            )
            return {
                exceptions.Error(
                    param_name='return.http_code',
                    reason='missing',
                )
            }
        return set()

    elif ti.is_literal_type(rt_http_code):
        # this is acceptable. Literals hold particular values inside of them
        # if user wants to have it that way -> go ahead.
        # axion however will not validate a specific values in Literal.
        # this is by design and due to:
        # - error responses that axion implements via exceptions
        literal_types = types.literal_types(rt_http_code)
        if not all(
                issubclass(lmt, model.HTTP_CODE_TYPE)
                for lmt in literal_types):
            return {
                exceptions.Error(
                    param_name='return.http_code',
                    reason=exceptions.CustomReason(
                        f'expected {repr(te.Literal)}[int]'),
                ),
            }
        return set()

    elif ti.is_new_type(rt_http_code):
        # not quite sure why user would like to alias that
        # but it is not a problem for axion as long `NewType` embedded type
        # is fine
        return _analyze_http_code(operation, rt_http_code.__supertype__)

    elif issubclass(rt_http_code, bool):
        # yeah, Python rocks -> bool is subclass of an int
        # not quite sure wh that happens, perhaps someone sometime
        # will answer that question
        return {
            exceptions.Error(
                param_name='return.http_code',
                reason=exceptions.IncorrectTypeReason(
                    expected=[model.HTTP_CODE_TYPE],
                    actual=bool,
                ),
            ),
        }
    else:

        try:
            assert issubclass(rt_http_code, model.HTTP_CODE_TYPE)
            return set()
        except (AssertionError, TypeError):
            ...
        return {
            exceptions.Error(
                param_name='return.http_code',
                reason=exceptions.IncorrectTypeReason(
                    actual=rt_http_code,
                    expected=[
                        type(None),
                        model.HTTP_CODE_TYPE,
                        t.NewType('HttpCode', model.HTTP_CODE_TYPE),
                        te.Literal,
                    ],
                ),
            ),
        }
Пример #8
0
def is_numpy_1_darray(type_: type) -> bool:
    return typing_inspect.is_new_type(
        type_) and type_.__name__ == "Numpy1DArray"
Пример #9
0
    def _validate_type_arg(self,
                           arg: str,
                           arg_type: Type,
                           *,
                           strict: bool = True,
                           allow_none_type: bool = False,
                           in_url: bool = False) -> None:
        """Validate the given type arg recursively

        :param arg: The name of the argument
        :param arg_type: The annotated type fo the argument
        :param strict: If true, does not allow `Any`
        :param allow_none_type: If true, allow `None` as the type for this argument
        :param in_url: This argument is passed in the URL
        """

        if typing_inspect.is_new_type(arg_type):
            return self._validate_type_arg(
                arg,
                arg_type.__supertype__,
                strict=strict,
                allow_none_type=allow_none_type,
                in_url=in_url,
            )

        if arg_type is Any:
            if strict:
                raise InvalidMethodDefinition(
                    f"Invalid type for argument {arg}: Any type is not allowed in strict mode"
                )
            return

        if typing_inspect.is_union_type(arg_type):
            # Make sure there is only one list and one dict in the union, otherwise we cannot process the arguments
            cnt: Dict[str, int] = defaultdict(lambda: 0)
            for sub_arg in typing_inspect.get_args(arg_type, evaluate=True):
                self._validate_type_arg(arg,
                                        sub_arg,
                                        strict=strict,
                                        allow_none_type=allow_none_type,
                                        in_url=in_url)

                if typing_inspect.is_generic_type(sub_arg):
                    # there is a difference between python 3.6 and >=3.7
                    if hasattr(sub_arg, "__name__"):
                        cnt[sub_arg.__name__] += 1
                    else:
                        cnt[sub_arg._name] += 1

            for name, n in cnt.items():
                if n > 1:
                    raise InvalidMethodDefinition(
                        f"Union of argument {arg} can contain only one generic {name}"
                    )

        elif typing_inspect.is_generic_type(arg_type):
            orig = typing_inspect.get_origin(arg_type)
            assert orig is not None  # Make mypy happy
            if not types.issubclass(orig, (list, dict)):
                raise InvalidMethodDefinition(
                    f"Type {arg_type} of argument {arg} can only be generic List or Dict"
                )

            args = typing_inspect.get_args(arg_type, evaluate=True)
            if len(args) == 0:
                raise InvalidMethodDefinition(
                    f"Type {arg_type} of argument {arg} must be have a subtype plain List or Dict is not allowed."
                )

            elif len(args) == 1:  # A generic list
                unsubscripted_arg = typing_inspect.get_origin(
                    args[0]) if typing_inspect.get_origin(args[0]) else args[0]
                assert unsubscripted_arg is not None  # Make mypy happy
                if in_url and (types.issubclass(unsubscripted_arg, dict)
                               or types.issubclass(unsubscripted_arg, list)):
                    raise InvalidMethodDefinition(
                        f"Type {arg_type} of argument {arg} is not allowed for {self.operation}, "
                        f"lists of dictionaries and lists of lists are not supported for GET requests"
                    )
                self._validate_type_arg(arg,
                                        args[0],
                                        strict=strict,
                                        allow_none_type=allow_none_type,
                                        in_url=in_url)

            elif len(args) == 2:  # Generic Dict
                if not types.issubclass(args[0], str):
                    raise InvalidMethodDefinition(
                        f"Type {arg_type} of argument {arg} must be a Dict with str keys and not {args[0].__name__}"
                    )
                unsubscripted_dict_value_arg = (typing_inspect.get_origin(
                    args[1]) if typing_inspect.get_origin(args[1]) else
                                                args[1])
                assert unsubscripted_dict_value_arg is not None  # Make mypy happy
                if in_url and (typing_inspect.is_union_type(args[1])
                               or types.issubclass(
                                   unsubscripted_dict_value_arg, dict)):
                    raise InvalidMethodDefinition(
                        f"Type {arg_type} of argument {arg} is not allowed for {self.operation}, "
                        f"nested dictionaries and union types for dictionary values are not supported for GET requests"
                    )

                self._validate_type_arg(arg,
                                        args[1],
                                        strict=strict,
                                        allow_none_type=True,
                                        in_url=in_url)

            elif len(args) > 2:
                raise InvalidMethodDefinition(
                    f"Failed to validate type {arg_type} of argument {arg}.")

        elif not in_url and types.issubclass(arg_type, VALID_SIMPLE_ARG_TYPES):
            pass
        elif in_url and types.issubclass(arg_type, VALID_URL_ARG_TYPES):
            pass
        elif allow_none_type and types.issubclass(arg_type, type(None)):
            # A check for optional arguments
            pass
        else:
            valid_types = ", ".join(
                [x.__name__ for x in VALID_SIMPLE_ARG_TYPES])
            raise InvalidMethodDefinition(
                f"Type {arg_type.__name__} of argument {arg} must be a either {valid_types} or a List of these types or a "
                "Dict with str keys and values of these types.")