Beispiel #1
0
def simplify_generics(obj1, obj2, two_element_transformers: List[Callable]):
    if not typing_inspect.is_generic_type(
            obj1) or not typing_inspect.is_generic_type(obj2):
        return NOT_SIMPLIFIED

    if typing_inspect.get_origin(obj1) != typing_inspect.get_origin(obj2):
        return NOT_SIMPLIFIED

    # print()
    # print('trying to simplify', obj1, obj2, sep='\n')

    transformed = []
    for arg1, arg2 in zip(obj1.__args__, obj2.__args__):
        # print('arg1, arg2: ', arg1, arg2)

        for transformer in [collapse_unions, *two_element_transformers]:
            transformed_obj = transformer(arg1, arg2)
            if transformed_obj is not NOT_SIMPLIFIED:
                transformed.append(transformed_obj)
                break
        else:
            return NOT_SIMPLIFIED

    result = typing_inspect.get_origin(obj1)[tuple(transformed)]

    # print('result', result)
    return result
Beispiel #2
0
    def _validate_return_type(self,
                              arg_type: Type,
                              *,
                              strict: bool = True) -> None:
        """Validate the return type"""
        # Note: we cannot call issubclass on a generic type!
        arg = "return type"

        if typing_inspect.is_generic_type(arg_type) and issubclass(
                typing_inspect.get_origin(arg_type), ReturnValue):
            self._validate_type_arg(arg,
                                    typing_inspect.get_args(arg_type,
                                                            evaluate=True)[0],
                                    strict=strict,
                                    allow_none_type=True)

        elif not typing_inspect.is_generic_type(arg_type) and isinstance(
                arg_type, type) and issubclass(arg_type, ReturnValue):
            raise InvalidMethodDefinition(
                "ReturnValue should have a type specified.")

        else:
            self._validate_type_arg(arg,
                                    arg_type,
                                    allow_none_type=True,
                                    strict=strict)
Beispiel #3
0
    def _validate_union_return(self, arg_type: Type, value: Any) -> None:
        """Validate a return with a union type
        :see: protocol.common.MethodProperties._validate_function_types
        """
        matching_type = None
        for t in typing_inspect.get_args(arg_type, evaluate=True):
            instanceof_type = t
            if typing_inspect.is_generic_type(t):
                instanceof_type = typing_inspect.get_origin(t)

            if isinstance(value, instanceof_type):
                if matching_type is not None:
                    raise exceptions.ServerError(
                        f"Return type is defined as a union {arg_type} for which multiple "
                        f"types match the provided value {value}"
                    )
                matching_type = t

        if matching_type is None:
            raise exceptions.BadRequest(
                f"Invalid return value, no matching type found in union {arg_type} for value type {type(value)}"
            )

        if typing_inspect.is_generic_type(matching_type):
            self._validate_generic_return(arg_type, matching_type)
 def do_explode(self, kind):
     if kind in basic_types or type(kind) is typing.TypeVar:
         return False
     if typing_inspect.is_generic_type(kind) and issubclass(typing_inspect.get_origin(kind), Sequence):
         return False
     if typing_inspect.is_generic_type(kind) and issubclass(typing_inspect.get_origin(kind), Mapping):
         return False
     self.clear()
     self.extend(Args(self.schema, kind))
     return True
Beispiel #5
0
def remove_empty_container(obj1, obj2):
    if not typing_inspect.is_generic_type(
            obj1) or not typing_inspect.is_generic_type(obj2):
        return NOT_SIMPLIFIED

    if obj1._gorg != obj2._gorg:
        return NOT_SIMPLIFIED

    if _is_empty(obj1):
        return obj2

    if _is_empty(obj2):
        return obj1

    return NOT_SIMPLIFIED
Beispiel #6
0
 def is_return_value_type(arg_type: Type) -> bool:
     if typing_inspect.is_generic_type(arg_type):
         origin = typing_inspect.get_origin(arg_type)
         assert origin is not None  # Make mypy happy
         return types.issubclass(origin, ReturnValue)
     else:
         return False
Beispiel #7
0
def type_from_annotation(annotation):
    if annotation == inspect._empty:
        return None
    elif annotation == t.Any:
        return AnyType()
    elif ti.is_generic_type(annotation):
        g_origin = ti.get_origin(annotation)
        g_args = ti.get_args(annotation)

        if g_origin in [list]:  # Better way?
            base_type = type_from_annotation(g_args[0])

            return ListType(type=base_type)

        elif g_origin in [dict]:  # Better way?
            key_type = type_from_annotation(g_args[0])
            value_type = type_from_annotation(g_args[1])

            return MapType(key_type=key_type, value_type=value_type)
    elif inspect.isclass(annotation):
        if issubclass(annotation, BaseResource):
            return ResourceType(type=annotation)
        else:
            return ScalarType(type=annotation)
    else:
        raise Exception(f'Unhandled annotation {annotation}')
Beispiel #8
0
def _generate_field_schema(field: Field, context: str,
                           schemas: Dict[str, Schema]) -> Tuple[bool, Schema]:
    is_optional, annotation = extract_optional_annotation(field.annotation)
    if is_schema(annotation):
        field_schema_name = _generate_schema(context, annotation, schemas)
        field_schema = Schema(all_of=[_make_schema_ref(field_schema_name)])

    elif is_generic_type(annotation):
        origin = get_origin(annotation)
        if origin in (list, List):
            arguments = get_args(annotation)
            if not arguments or arguments == [Any] or is_typevar(arguments[0]):
                field_schema = _generate_primitive_schema(list)

            else:
                item_schema_name = _generate_schema(context, arguments[0],
                                                    schemas)
                field_schema = Schema("array",
                                      items=_make_schema_ref(item_schema_name))

        elif origin in (dict, Dict):
            # TODO: Improve support for dicts.
            field_schema = _generate_primitive_schema(dict)

    else:
        field_schema = _generate_primitive_schema(annotation)

    if field_schema is not None:
        field_schema.description = field.description
        for option, value in field.validator_options.items():
            if option in Schema._FIELDS:
                setattr(field_schema, option, value)

    return is_optional, field_schema
Beispiel #9
0
def check_args(cls: Type, generic_base_class: Type, typevar: TypeVar) -> int:
    """
    Performs argument-validation for get_argument_to_typevar.

    :param cls:                 The sub-class specifying the argument.
    :param generic_base_class:  The generic base-class specifying the type variable.
    :param typevar:             The type variable to get the argument for.
    :return:                    The index of the typevar in the base class' parameters.
    """
    # Make sure the class derives from the base-class
    if not issubclass(cls, generic_base_class):
        raise ValueError(
            f"{cls.__name__} does not derive from {generic_base_class.__name__}"
        )

    # Make sure the base class is generic
    if not typing_inspect.is_generic_type(generic_base_class):
        raise TypeError(f"{generic_base_class.__name__} is not a generic type")

    # Get the type parameters to the generic base class
    parameters = typing_inspect.get_parameters(generic_base_class)

    # Make sure the type variable is a parameter to the base class
    if typevar not in parameters:
        raise ValueError(
            f"{typevar} is not a generic parameter of {generic_base_class.__name__}"
        )

    return parameters.index(typevar)
Beispiel #10
0
    def _rewrite(self, typ: type):
        if isinstance(typ, _Any):
            return typ

        arg_classes = getattr(typ, '__args__', [])
        if not arg_classes:
            return typ

        if typing_inspect.is_generic_type(typ) or typing_inspect.is_tuple_type(
                typ):
            gorg = typing_inspect.get_origin(typ)

            processed_args = []
            for arg_class in arg_classes:
                processed = self._rewrite(arg_class)
                if processed is NOT_SIMPLIFIED:
                    processed = arg_class

                processed_args.append(processed)

            return gorg[tuple(processed_args)]

        processed_args = []
        for arg_class in arg_classes:
            rewritten = self._rewrite(arg_class)
            if rewritten is NOT_SIMPLIFIED:
                processed_args.append(arg_class)
                continue

            processed_args.append(rewritten)

        new_union = make_union(processed_args)
        new_union = self.union_rewriter.rewrite_Union(new_union)

        return new_union
Beispiel #11
0
def extract_generic(tp: Type[T]) -> Tuple[bool, Type[T], Tuple[Type, ...]]:
    """
    Helper function that checks if the given type is generic,
    and if it is, expands it.
    
    Args:
        tp: `Type[T]` - potentially generic type.
    
    Returns:
        Returns a tuple of 3 values.
        
        - `bool`: **True** if the given type is `Generic`; **False** otherwise.
        - `Type[R]`: The class *T*, if *T* is not optional; and *R* if *T* is `Optional[R]`.
        - `Tuple[Type, ...]`: The tuple of class parameters used for *T*'s creation; empty tuple if it is not generic.
    
    Examples:
        ```python
        extract_generic(dict)                      # => (False, dict,    ())
        extract_generic(Dict[A, B])                # => (True,  Dict,    (A, B))
        extract_generic(Optional[int])             # => (True,  Union,   (int, None))
        extract_generic(MyClass[str])              # => (True,  MyClass, (str))
        extract_generic(Union[MyClass[str], None]) # => (True,  Union,   (MyClass[str], None))
        ```
    """

    if (isinstance(tp, GenericProxy)):
        # noinspection PyTypeChecker
        return True, tp.base, tp.args

    if (is_generic_type(tp)):
        base = get_origin(tp)
        return True, base, get_args(tp, evaluate=True)
    else:
        return False, tp, tuple()
Beispiel #12
0
def _generate_field_schema(field_name: str, field: Field,
                           schemas: Dict[str, Schema]) -> Tuple[bool, Schema]:
    is_optional, annotation = extract_optional_annotation(field.annotation)
    if is_schema(annotation):
        field_schema_name = _generate_schema(annotation, schemas)
        field_schema = Schema(ref=_make_ref_path(field_schema_name))

    elif is_generic_type(annotation):
        origin = get_origin(annotation)
        if origin in _LIST_TYPES:
            arguments = get_args(annotation)
            if arguments and is_schema(arguments[0]):
                item_schema_name = _generate_schema(arguments[0], schemas)
                field_schema = Schema("array",
                                      items=_make_schema_ref(item_schema_name))

            else:
                field_schema = _generate_primitive_schema(annotation)

        elif origin in _DICT_TYPES:
            # TODO: Add support for additionalFields.
            field_schema = _generate_primitive_schema(dict)

        else:  # pragma: no cover
            raise ValueError(
                f"Unsupported type {origin} for field {field.name!r}.")

    elif is_union_type(annotation):
        sub_schemas = []
        for arg in get_args(annotation):
            _, sub_schema = _generate_field_schema("", Field(annotation=arg),
                                                   schemas)
            sub_schemas.append(dump_schema(sub_schema, sparse=True))

        field_schema = Schema(any_of=sub_schemas)

    else:
        field_schema = _generate_primitive_schema(annotation)

    if field_schema is not None:
        field_schema.description = field.description

        if field.request_name != field.response_name:
            if field_name == field.request_name:
                field_schema.write_only = True

            else:
                field_schema.read_only = True

        elif field.response_only:
            field_schema.read_only = True

        elif field.request_only:
            field_schema.write_only = True

        for option, value in field.validator_options.items():
            if option in Schema._FIELDS:
                setattr(field_schema, option, value)

    return is_optional, field_schema
Beispiel #13
0
def _get_iterable_of(t: Type) -> Optional[Type]:
    if not typing_inspect.is_generic_type(t):
        return None
    origin = typing_inspect.get_origin(t)
    args = typing_inspect.get_args(t, evaluate=True)
    if origin in (list, List, Iterable, Iterator):
        return args[0]
    return None
Beispiel #14
0
def inspect_type(t) -> InspectedType:
    """Returns basic information on a type as an InspectedType"""
    args = typing_inspect.get_args(t)
    if typing_inspect.is_generic_type(t):
        origin = typing_inspect.get_origin(t)
    else:
        origin = t
    return InspectedType(type=t, origin=origin, args=args)
Beispiel #15
0
def _check_annotation(f_type, f_fullname, f_default):
    if typing_inspect.is_tuple_type(f_type):
        if f_default is not None:
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'default is defined for tuple type')

        f_default = tuple

    elif typing_inspect.is_union_type(f_type):
        for t in typing_inspect.get_args(f_type, evaluate=True):
            _check_annotation(t, f_fullname, f_default)

    elif typing_inspect.is_generic_type(f_type):
        if f_default is not None:
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'default is defined for container type '
                               f'{f_type!r}')

        ot = typing_inspect.get_origin(f_type)
        if ot is None:
            raise RuntimeError(
                f'cannot find origin of a generic type {f_type}')

        if ot in (list, List, collections.abc.Sequence):
            f_default = list
        elif ot in (set, Set):
            f_default = set
        elif ot in (frozenset, FrozenSet):
            f_default = frozenset
        elif ot in (dict, Dict):
            f_default = dict
        else:
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'{f_type!r} is not supported')

    elif f_type is not None:
        if f_type is Any:
            f_type = object

        if not isinstance(f_type, type):
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'{f_type!r} is not a type')

        if typeutils.is_container_type(f_type):
            if f_default is not None:
                raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                                   f'default is defined for container type '
                                   f'{f_type!r}')
            # Make sure that we can actually construct an empty
            # version of this type before we decide it is the default.
            try:
                f_type()
            except TypeError:
                pass
            else:
                f_default = f_type

    return f_default
Beispiel #16
0
def _generic_to_tuple(
    type_: _tp.Type[_tp.Any]
) -> _tp.Union[_tp.Type[_tp.Any], _tp.Tuple[_tp.Type[_tp.Any], ...]]:
    if _typing_inspect.is_generic_type(type_):
        class_ = _typing_inspect.get_origin(type_)
        types = _typing_inspect.get_args(type_)
        return (class_, *types)

    return type_
Beispiel #17
0
def _type_from_value(value, ctx):
    if isinstance(value, KnownValue):
        return _type_from_runtime(value.val, ctx)
    elif isinstance(value, _SubscriptedValue):
        if not isinstance(value.root, KnownValue):
            ctx.show_error("Cannot resolve subscripted annotation: %s" %
                           (value.root, ))
            return UNRESOLVED_VALUE
        root = value.root.val
        if root is typing.Union:
            return unite_values(
                *[_type_from_value(elt, ctx) for elt in value.members])
        elif is_typing_name(root, "Literal"):
            if all(isinstance(elt, KnownValue) for elt in value.members):
                return unite_values(value.members)
            else:
                ctx.show_error(
                    "Arguments to Literal[] must be literals, not %s" %
                    (value.members, ))
                return UNRESOLVED_VALUE
        elif root is typing.Optional:
            if len(value.members) != 1:
                ctx.show_error("Optional[] takes only one argument")
                return UNRESOLVED_VALUE
            return unite_values(KnownValue(None),
                                _type_from_value(value.members[0], ctx))
        elif root is typing.Type:
            if len(value.members) != 1:
                ctx.show_error("Type[] takes only one argument")
                return UNRESOLVED_VALUE
            argument = _type_from_value(value.members[0], ctx)
            if isinstance(argument, TypedValue) and isinstance(
                    argument.typ, type):
                return SubclassValue(argument.typ)
            return TypedValue(type)
        elif typing_inspect.is_generic_type(root):
            origin = typing_inspect.get_origin(root)
            if getattr(origin, "__extra__", None) is not None:
                origin = origin.__extra__
            return GenericValue(
                origin, [_type_from_value(elt, ctx) for elt in value.members])
        elif isinstance(root, type):
            return GenericValue(
                root, [_type_from_value(elt, ctx) for elt in value.members])
        else:
            # In Python 3.9, generics are implemented differently and typing.get_origin
            # can help.
            origin = get_origin(root)
            if origin is not None:
                return GenericValue(
                    origin,
                    [_type_from_value(elt, ctx) for elt in value.members])
            ctx.show_error("Unrecognized subscripted annotation: %s" %
                           (root, ))
            return UNRESOLVED_VALUE
    else:
        return UNRESOLVED_VALUE
Beispiel #18
0
def _maybe_node_for_dict(
    typ: Type[iface.IType],
    overrides: OverridesT,
    memo: MemoType,
    forward_refs: ForwardRefs,
    supported_type=frozenset({
        dict,
        collections.abc.Mapping,
        pyt.PMap,
    }),
    supported_origin=frozenset({
        Dict,
        dict,
        collections.abc.Mapping,
        pyt.PMap,
    })
) -> Tuple[Optional[schema.nodes.SchemaNode], MemoType, ForwardRefs]:
    """ This is mainly for cases when a user has manually
    specified that a field should be a dictionary, rather than a
    strict structure, possibly due to dynamic nature of keys
    (for instance, python logging settings that have an infinite
    set of possible attributes).
    """
    rv = None
    # This is a hack for Python 3.9
    if insp.is_generic_type(typ):
        generic_bases = [get_origin_39(x) for x in insp.get_generic_bases(typ)]
    else:
        generic_bases = []

    typ = dict if is_39_deprecated_dict(typ) else typ

    if typ in supported_type or get_origin_39(
            typ) in supported_origin or are_generic_bases_match(
                generic_bases, supported_origin):
        schema_node_type = schema.nodes.PMapSchema if is_pmap(
            typ) else schema.nodes.SchemaNode

        if generic_bases:
            # python 3.9 args
            key_type, value_type = typ.__args__
        else:
            try:
                key_type, value_type = insp.get_args(typ)
            except ValueError:
                # Mapping doesn't provide key/value types
                key_type, value_type = Any, Any

        key_node, memo, forward_refs = decide_node_type(
            key_type, overrides, memo, forward_refs)
        value_node, memo, forward_refs = decide_node_type(
            value_type, overrides, memo, forward_refs)
        mapping_type = schema.types.TypedMapping(key_node=key_node,
                                                 value_node=value_node)
        rv = schema_node_type(mapping_type)
    return rv, memo, forward_refs
Beispiel #19
0
def is_type(type_or_hint) -> bool:
    """
    returns whether the type or hint is a Type typehint
    """
    if not is_generic_type(type_or_hint):
        return False
    if NEW_TYPING:
        return get_origin(type_or_hint) is type
    else:
        return getattr(type_or_hint, "__extra__", None) is type
Beispiel #20
0
 def __call__(self, tp):
     if inspect.isclass(tp):
         return self.iter_mro(tp)
     if ti.is_optional_type(tp):
         return self.iter_optional(tp)
     if ti.is_tuple_type(tp):
         return self.iter_generic(tp)
     if ti.is_union_type(tp):
         return self.iter_generic(tp)
     if ti.is_generic_type(tp):
         return self.iter_generic(tp)
Beispiel #21
0
    def __set_name__(self, cls: 'ObjectMeta', name: str) -> None:
        super().__set_name__(cls, name)
        if not self.pipe:
            ann = get_type_hints(cls).get(name, None)
            if (not is_generic_type(ann) and get_origin(ann) is not type(self)
                    and len(get_args(ann)) != 1):
                raise TypeError('no type or conversions '
                                'specified for member {!r}'.format(name))
            self.pipe = (jsontype(get_args(ann)[0]), )

        self.json_to = _compose(*(p[0] for p in self.pipe))
        self.for_json = _compose(*(p[1] for p in reversed(self.pipe)))
Beispiel #22
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
Beispiel #23
0
def decode_generic_dict(decoder, typ, json_value):
    if not (inspect.is_generic_type(typ) and inspect.get_origin(typ) == dict):
        return Unsupported
    check_type(dict, json_value)
    key_type, value_type = inspect.get_args(typ)
    if key_type != str:
        raise JsonError(
            f'Dict key type {key_type} is not supported for JSON deserialization - key should be str'
        )
    return {
        key: decoder.decode(value_type, value)
        for (key, value) in json_value.items()
    }
Beispiel #24
0
    def _init_parametric_user(cls) -> None:
        """Initialize an indirect descendant of ParametricType."""

        # For ParametricType grandchildren we have to deal with possible
        # TypeVar remapping and generally check for type sanity.

        ob = getattr(cls, '__orig_bases__', ())
        for b in ob:
            if (isinstance(b, type) and issubclass(b, ParametricType)
                    and b is not ParametricType):
                raise TypeError(
                    f'{cls.__name__}: missing one or more type arguments for'
                    f' base {b.__name__!r}')

            if not typing_inspect.is_generic_type(b):
                continue

            org = typing_inspect.get_origin(b)
            if not isinstance(org, type):
                continue
            if not issubclass(org, ParametricType):
                continue

            base_params = getattr(org, '__parameters__', ())

            args = typing_inspect.get_args(b)
            expected = len(base_params)
            if len(args) != expected:
                raise TypeError(
                    f'{b.__name__} expects {expected} type arguments'
                    f' got {len(args)}')

            base_map = dict(cls._type_param_map)
            subclass_map = {}

            for i, arg in enumerate(args):
                if not typing_inspect.is_typevar(arg):
                    raise TypeError(f'{b.__name__} expects all arguments to be'
                                    f' TypeVars')

                base_typevar = base_params[i]
                attr = base_map.get(base_typevar)
                if attr is not None:
                    subclass_map[arg] = attr

            if len(subclass_map) != len(base_map):
                raise TypeError(
                    f'{cls.__name__}: missing one or more type arguments for'
                    f' base {org.__name__!r}')

            cls._type_param_map = subclass_map
Beispiel #25
0
    def validate(self, value: Optional[Any]) -> _T:
        """Validate and possibly transform the given value.

        Raises:
          FieldValidationError: When the value is not valid.
        """
        is_optional, annotation = extract_optional_annotation(self.annotation)
        # Distinguishing between missing values and null values is
        # important.  Optional types can have None as a value whereas
        # types with a default cannot.  Additionally, it's possible to
        # have an optional type without a default value.
        if value is Missing:
            if self.default is not Missing:
                return self.default

            elif self.default_factory:
                return self.default_factory()

            elif is_optional:
                return None

            raise FieldValidationError("this field is required")

        if value is None:
            if not is_optional:
                raise FieldValidationError("this field cannot be null")

            return value

        if annotation not in (Any,) and \
           not is_forward_ref(annotation) and \
           not is_generic_type(annotation) and \
           not is_union_type(annotation) and \
           not is_typevar(annotation) and \
           not is_schema(annotation) and \
           not isinstance(value, annotation):
            if not self.allow_coerce:
                raise FieldValidationError(
                    f"unexpected type {type(value).__name__}")

            try:
                value = annotation(value)
            except Exception:
                raise FieldValidationError(
                    f"value could not be coerced to {annotation.__name__}")

        if self.validator:
            return self.validator.validate(self, value,
                                           **self.validator_options)
        return value
def get_all_subclasses(typ,
                       recursive: bool = True,
                       _memo=None) -> Sequence[Type[Any]]:
    """
    Returns all subclasses, and supports generic types. It is recursive by default
    See discussion at https://github.com/Stewori/pytypes/issues/31

    :param typ:
    :param recursive: a boolean indicating whether recursion is needed
    :param _memo: internal variable used in recursion to avoid exploring subclasses that were already explored
    :return:
    """
    _memo = _memo or set()

    # if we have collected the subclasses for this already, return
    if typ in _memo:
        return []

    # else remember that we have collected them, and collect them
    _memo.add(typ)
    if is_generic_type(typ):
        # We now use get_origin() to also find all the concrete subclasses in case the desired type is a generic
        sub_list = get_origin(typ).__subclasses__()
    else:
        sub_list = typ.__subclasses__()

    # recurse
    result = []
    for t in sub_list:
        # only keep the origins in the list
        to = get_origin(t) or t
        try:
            if to is not typ and to not in result and is_subtype(
                    to, typ, bound_typevars={}):
                result.append(to)
        except:
            # catching an error with is_subtype(Dict, Dict[str, int], bound_typevars={})
            pass

    # recurse
    if recursive:
        for typpp in sub_list:
            for t in get_all_subclasses(typpp, recursive=True, _memo=_memo):
                # unfortunately we have to check 't not in sub_list' because with generics strange things happen
                # also is_subtype returns false when the parent is a generic
                if t not in sub_list and is_subtype(t, typ, bound_typevars={}):
                    result.append(t)

    return result
Beispiel #27
0
def _check_annotation(f_type, f_fullname, f_default):
    if typing_inspect.is_tuple_type(f_type):
        if f_default is not None:
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'default is defined for tuple type')

        f_default = tuple

    elif typing_inspect.is_union_type(f_type):
        for t in typing_inspect.get_args(f_type, evaluate=True):
            _check_annotation(t, f_fullname, f_default)

    elif typing_inspect.is_generic_type(f_type):
        if f_default is not None:
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'default is defined for container type '
                               f'{f_type!r}')

        ot = typing_inspect.get_origin(f_type)
        if ot is None:
            raise RuntimeError(
                f'cannot find origin of a generic type {f_type}')

        if ot in (list, typing.List):
            f_default = list
        elif ot in (set, typing.Set):
            f_default = set
        elif ot in (frozenset, typing.FrozenSet):
            f_default = frozenset
        elif ot in (dict, typing.Dict):
            f_default = dict
        else:
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'{f_type!r} is not supported')

    elif f_type is not None:
        if not isinstance(f_type, type):
            raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                               f'{f_type!r} is not a type')

        if typeutils.is_container_type(f_type):
            if f_default is not None:
                raise RuntimeError(f'invalid type annotation on {f_fullname}: '
                                   f'default is defined for container type '
                                   f'{f_type!r}')
            f_default = f_type

    return f_default
Beispiel #28
0
    def _build_return_value_wrapper(self, url_method_properties: MethodProperties) -> Optional[Dict[str, MediaType]]:
        return_type = inspect.signature(url_method_properties.function).return_annotation

        if return_type is None or return_type == inspect.Signature.empty:
            return None

        return_properties: Optional[Dict[str, Schema]] = None

        if return_type == ReturnValue or is_generic_type(return_type) and get_origin(return_type) == ReturnValue:
            # Dealing with the special case of ReturnValue[...]
            links_type = self.type_converter.get_openapi_type(Dict[str, str])
            links_type.title = "Links"
            links_type.nullable = True

            warnings_type = self.type_converter.get_openapi_type(List[str])
            warnings_type.title = "Warnings"

            return_properties = {
                "links": links_type,
                "metadata": Schema(
                    title="Metadata",
                    nullable=True,
                    type="object",
                    properties={
                        "warnings": warnings_type,
                    },
                ),
            }

            type_args = get_args(return_type, evaluate=True)
            if not type_args or len(type_args) != 1:
                raise RuntimeError(
                    "ReturnValue definition should take one type Argument, e.g. ReturnValue[None].  "
                    f"Got this instead: {type_args}"
                )

            if not url_method_properties.envelope:
                raise RuntimeError("Methods returning a ReturnValue object should always have an envelope")

            if type_args[0] != NoneType:
                return_properties[url_method_properties.envelope_key] = self.type_converter.get_openapi_type(type_args[0])

        else:
            openapi_return_type = self.type_converter.get_openapi_type(return_type)
            if url_method_properties.envelope:
                return_properties = {url_method_properties.envelope_key: openapi_return_type}

        return {"application/json": MediaType(schema=Schema(type="object", properties=return_properties))}
Beispiel #29
0
def is_list_type(tp) -> bool:
    """
    Test if the type is a generic list type, including subclasses excluding
    non-generic classes.
    Examples::
    
    is_list_type(int) == False
    is_list_type(list) == False
    is_list_type(List) == True
    is_list_type(List[str, int]) == True
    class MyClass(List[str]):
        ...
    is_list_type(MyClass) == True
    """

    return is_generic_type(tp) and issubclass(get_origin(tp) or tp, List)
Beispiel #30
0
    def to_json_value(self):
        dct = {}
        dct['_tname'] = self.__class__.__name__

        for f in dataclasses.fields(self):
            f_type = f.type
            value = getattr(self, f.name)
            if (isinstance(f_type, type)
                    and issubclass(f_type, CompositeConfigType)
                    and value is not None):
                value = value.to_json_value()
            elif typing_inspect.is_generic_type(f_type):
                value = list(value)

            dct[f.name] = value

        return dct