Пример #1
0
def is_instance_of_type(_object: Any, required_type: Type) -> Optional[bool]:
    """
    Check if `object` is of type `required_type`.

    Works same as `isinstance`, but also has limited support of typing
    generics (such as Union, Optional, etc).

    Works on small subset of types, can fail on complex types.
    See tests (tests/tests_unit/test_type_utils.py) for examples.
    """
    type_to_check_first = required_type
    if is_optional_type(required_type) or is_union_type(required_type):
        type_to_check_first = get_args(required_type)
    try:
        return isinstance(_object, type_to_check_first)
    except TypeError:
        pass

    if is_optional_type(required_type):
        if _object is None:
            return True
        required_type = type_to_check_first[0]  # content of Optional[...]

    type_to_checker_map = {
        list: _is_instance_of_list_type,
        dict: _is_instance_of_dict_type,
        Mapping: _is_instance_of_dict_type,
    }
    if get_origin(required_type) in type_to_checker_map:
        return type_to_checker_map[get_origin(required_type)](_object,
                                                              required_type)
    return None
Пример #2
0
def _is_instance_of_dict_type(_object: Any, required_dict_type: Type) -> bool:
    key_type, value_type = get_args(required_dict_type)
    if is_optional_type(key_type) or is_union_type(key_type):
        key_type = get_args(key_type)
    if is_optional_type(value_type) or is_union_type(value_type):
        value_type = get_args(value_type)

    return not (not isinstance(_object, dict) or any(
        not isinstance(k, key_type)
        for k in _object.keys()) or any(not isinstance(v, value_type)
                                        for v in _object.values()))
Пример #3
0
def is_subclass(lhs: Type, rhs: Type) -> object:
    if is_sequence_type(lhs):
        if not is_sequence_type(rhs):
            return False
        return is_subclass(unpack_type_argument(lhs), unpack_type_argument(rhs))

    if is_optional_type(lhs):
        if not is_optional_type(rhs):
            return False
        return is_subclass(unpack_type_argument(lhs), unpack_type_argument(rhs))

    return issubclass(lhs, rhs)
Пример #4
0
    def __init__(
        self,
        dependency: Union[T, str],
        *,
        namespace: str = None,
        group: str = None,
        exclude_groups: Sequence[str] = None,
        lazy: bool = False,
    ):
        optional = False
        multiple = False

        if typing_inspect.is_optional_type(dependency):
            dependency = typing_inspect.get_args(dependency, evaluate=True)[0]
            optional = True
        elif typing_inspect.is_union_type(dependency):
            raise TypeError(
                f"Autowired Union can only be used to indicate"
                f" optional autowiring in the forms 'Union[T, None]' or"
                f" 'Optional[T]'")

        if is_sequence(typing_inspect.get_origin(dependency) or dependency):
            subscripted_types = typing_inspect.get_args(dependency,
                                                        evaluate=True)
            if subscripted_types == typing_inspect.get_args(Sequence):
                raise TypeError(f"Type not defined for Autowired list")
            subscripted_type = subscripted_types[0]
            if typing_inspect.is_optional_type(subscripted_type):
                raise TypeError(
                    f"List of Optional is invalid for autowiring. Use"
                    f" 'Autowired(Optional[List[...]])' instead.")
            elif typing_inspect.is_union_type(subscripted_type):
                raise TypeError(
                    f"Only one type should be defined for Autowired list")
            dependency = subscripted_type
            multiple = True
        elif is_raw_sequence(dependency):
            if len(dependency) != 1:
                raise TypeError(
                    f"Only one type should be defined for Autowired"
                    f" {dependency.__class__.__qualname__}")
            dependency = dependency[0]
            multiple = True

        self.optional = optional
        self.multiple = multiple
        self.dependency = sanitize_if_forward_ref(dependency)
        self.namespace = namespace
        self.group = group
        self.exclude_groups = exclude_groups
        self.lazy = lazy
Пример #5
0
    def structure_attrs_fromdict(self, obj: typing.Mapping,
                                 cl: typing.Type) -> typing.Any:
        """Instantiate an attrs class from a mapping (dict)."""
        conv_obj = {}
        dispatch = self._structure_func.dispatch
        for a in attr.fields(cl):
            # We detect the type by metadata.
            type_ = a.type
            if type_ is None:
                # No type.
                continue
            name = a.name
            try:
                val = obj[name]
            except KeyError:
                if typing_inspect.is_optional_type(type_):
                    if a.default in (missing, attr.NOTHING):
                        val = None
                    else:
                        val = a.default
                elif a.default in (missing, attr.NOTHING):
                    raise ValueError("Attribute is missing", a.name)
                else:
                    continue

            if a.converter is None:
                val = dispatch(type_)(val, type_)

            conv_obj[name] = val

        return cl(**conv_obj)
Пример #6
0
    def _parse_annotation(self, raw_annotation: Type) -> None:
        """Parse key information from annotation.

        :param annotation: A subscripted type.
        :returns: Annotation
        """
        self.raw_annotation = raw_annotation
        self.origin = self.arg = None

        self.optional = typing_inspect.is_optional_type(raw_annotation)
        if self.optional and typing_inspect.is_union_type(raw_annotation):
            # Annotated with Optional or Union[..., NoneType]
            # get_args -> (pandera.typing.Index[str], <class 'NoneType'>)
            raw_annotation = typing_inspect.get_args(raw_annotation)[0]

        self.origin = typing_inspect.get_origin(raw_annotation)
        # Replace empty tuple returned from get_args by None
        args = typing_inspect.get_args(raw_annotation) or None
        self.arg = args[0] if args else args

        self.metadata = getattr(self.arg, "__metadata__", None)
        if self.metadata:
            self.arg = typing_inspect.get_args(self.arg)[0]

        self.literal = typing_inspect.is_literal_type(self.arg)
        if self.literal:
            self.arg = typing_inspect.get_args(self.arg)[0]

        self.default_dtype = getattr(raw_annotation, "default_dtype", None)
Пример #7
0
def is_opt(typ) -> bool:
    """
    Test if the type is `typing.Optional`.
    """
    args = typing_inspect.get_args(typ)
    return typing_inspect.is_optional_type(typ) and len(
        args) == 2 and not is_none(args[0]) and is_none(args[1])
Пример #8
0
    def _parse_annotation(self, raw_annotation: Type) -> None:
        """Parse key information from annotation.

        :param annotation: A subscripted type.
        :returns: Annotation
        """
        self.raw_annotation = raw_annotation
        self.origin = self.arg = None

        self.optional = typing_inspect.is_optional_type(raw_annotation)
        if self.optional and typing_inspect.is_union_type(raw_annotation):
            # Annotated with Optional or Union[..., NoneType]
            if LEGACY_TYPING:  # pragma: no cover
                # get_args -> ((pandera.typing.Index, <class 'str'>), <class 'NoneType'>)
                self.origin, self.arg = typing_inspect.get_args(
                    raw_annotation)[0]
            # get_args -> (pandera.typing.Index[str], <class 'NoneType'>)
            raw_annotation = typing_inspect.get_args(raw_annotation)[0]

        if not (self.optional and LEGACY_TYPING):
            self.origin = typing_inspect.get_origin(raw_annotation)
            args = typing_inspect.get_args(raw_annotation)
            self.arg = args[0] if args else args

        self.metadata = getattr(self.arg, "__metadata__", None)
        if self.metadata:
            self.arg = typing_inspect.get_args(self.arg)[0]

        self.literal = typing_inspect.is_literal_type(self.arg)
        if self.literal:
            self.arg = typing_inspect.get_args(self.arg)[0]
Пример #9
0
def get_settings_def(type_def: Type[Generic]) -> Type[Settings]:
    """Get settings definition from object type definition.

    :param type_def:
    :return:
    """

    sig = inspect.signature(type_def.__init__)
    try:
        param = sig.parameters["settings"]
    except KeyError:
        raise Arcor2Exception("Type has no settings.")

    if typing_inspect.is_optional_type(param.annotation):
        settings_cls = typing_inspect.get_args(param.annotation)[0]
    else:
        settings_cls = param.annotation

    try:
        if not issubclass(settings_cls, Settings):
            raise Arcor2Exception("Settings have invalid type.")
    except TypeError:
        raise Arcor2Exception("Settings have invalid annotation.")

    if not is_dataclass(settings_cls):
        raise Arcor2Exception("Settings misses @dataclass decorator.")

    return settings_cls
Пример #10
0
def parse_annotation(raw_annotation: Type) -> AnnotationInfo:
    """Parse key information from annotation.

    :param annotation: A subscripted type.
    :returns: Annotation
    """
    optional = typing_inspect.is_optional_type(raw_annotation)
    if optional:
        # e.g: Typing.Union[pandera.typing.Index[str], NoneType]
        if _LEGACY_TYPING:  # pragma: no cover
            # get_args -> ((pandera.typing.Index, <class 'str'>), <class 'NoneType'>)
            origin, arg = typing_inspect.get_args(raw_annotation)[0]
            return AnnotationInfo(origin, arg, optional)
        # get_args -> (pandera.typing.Index[str], <class 'NoneType'>)
        raw_annotation = typing_inspect.get_args(raw_annotation)[0]

    origin = typing_inspect.get_origin(raw_annotation)
    args = typing_inspect.get_args(raw_annotation)
    arg = args[0] if args else args

    literal = typing_inspect.is_literal_type(arg)
    if literal:
        arg = typing_inspect.get_args(arg)[0]

    return AnnotationInfo(origin=origin,
                          arg=arg,
                          optional=optional,
                          literal=literal)
Пример #11
0
    def _parse_annotation(self, raw_annotation: Type) -> None:
        """Parse key information from annotation.

        :param annotation: A subscripted type.
        :returns: Annotation
        """
        self.raw_annotation = raw_annotation

        self.optional = typing_inspect.is_optional_type(raw_annotation)
        if self.optional:
            # e.g: Typing.Union[pandera.typing.Index[str], NoneType]
            if _LEGACY_TYPING:  # pragma: no cover
                # get_args -> ((pandera.typing.Index, <class 'str'>), <class 'NoneType'>)
                self.origin, self.arg = typing_inspect.get_args(
                    raw_annotation)[0]
            # get_args -> (pandera.typing.Index[str], <class 'NoneType'>)
            raw_annotation = typing_inspect.get_args(raw_annotation)[0]

        if not (self.optional and _LEGACY_TYPING):
            self.origin = typing_inspect.get_origin(raw_annotation)
            args = typing_inspect.get_args(raw_annotation)
            self.arg = args[0] if args else args

        self.literal = typing_inspect.is_literal_type(self.arg)
        if self.literal:
            self.arg = typing_inspect.get_args(self.arg)[0]
Пример #12
0
def config_repr_callable_args(
    init,
    param_dict=None,
    skip_self=False,
    only_required_args=None,
    literal_defaults=None,
):
    if only_required_args is None:
        only_required_args = ONLY_REQUIRED_ARGS
    if literal_defaults is None:
        literal_defaults = LITERAL_DEFAULTS

    if isinstance(init, Signature):
        sig = init
        globals_ = None
    else:
        sig = signature(init)
        globals_ = get_globals(init)

    concretize = param_dict or globals_
    append_positionals = has_varargs(sig)
    arg_reprs, kwarg_reprs = [], {}
    parameter_iter = iter(sig.parameters.items())
    if skip_self:
        next(parameter_iter)

    for name, param in parameter_iter:
        if literal_defaults and (param.default not in (Parameter.empty, None)):
            r = param.default
        else:
            if concretize:
                type_ = fully_concretize_type(param.annotation, param_dict,
                                              globals_)
            else:
                type_ = param.annotation
            r = config_repr(type_)

        if param.kind is Parameter.POSITIONAL_ONLY:
            arg_reprs.append(r)
        elif param.kind is Parameter.VAR_POSITIONAL:
            arg_reprs.extend([r, ellipsis_])
        elif param.kind is Parameter.VAR_KEYWORD:
            kwarg_reprs[ellipsis_] = r
        elif (only_required_args and param.default is not Parameter.empty
              and is_optional_type(param.annotation)):
            # only skip once we're past the positionals, to avoid skipping args
            continue
        elif param.kind is Parameter.KEYWORD_ONLY:
            kwarg_reprs[name] = r
        else:
            if append_positionals:
                arg_reprs.append(r)
            else:
                kwarg_reprs[name] = r

    repr_ = {KWARGS_KEY: kwarg_reprs}
    if arg_reprs:
        repr_[ARGS_KEY] = arg_reprs

    return repr_, sig
Пример #13
0
def get_optional(tp: Type):
    if is_optional_type(tp):
        if get_origin(tp) == Union:
            for t in get_args(tp):
                if t is None:
                    continue

                return t
Пример #14
0
def _from_json_like(type_hint, value):
    optional = is_optional_type(type_hint)

    (real_type, ) = ((t for t in get_args(type_hint)
                      if not is_none_type(t)) if optional else (type_hint, ))

    if real_type is Any:
        return value

    return from_json_like(real_type, value, optional)
Пример #15
0
def optional_core(t: Type) -> Tuple[bool, Type]:
    """
    :return  (is_optional, t.core | t | NoneType)
    """
    if t is type(None):
        return True, type(None)
    if is_optional_type(t):
        first, second = get_args(t)
        return True, (second if isinstance(None, first) else first)
    else:
        return False, t
Пример #16
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)
Пример #17
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
Пример #18
0
def auto_any(cls) -> schema.Schema:
    if inspect.isclass(cls):
        if issubclass(cls, bool):
            return schema.BooleanSchema()
        elif issubclass(cls, float):
            return schema.NumberSchema()
        elif issubclass(cls, int):
            return schema.IntSchema()
        elif issubclass(cls, str):
            return schema.StringSchema()
        elif issubclass(cls, datetime):
            return schema.StringSchema(format='date-time')
        elif issubclass(cls, date):
            return schema.StringSchema(format='date')
        elif issubclass(cls, timedelta):
            return schema.NumberSchema(format='double')
        elif issubclass(cls, List):
            vt, = get_args(cls)

            subschema = auto_any(vt)

            return schema.ArraySchema(items=subschema)
        elif issubclass(cls, Dict):
            kt, vt = get_args(cls)

            if not issubclass(kt, str):
                raise NotImplementedError(('class3', cls, kt))

            subschema = auto_any(vt)

            return ObjectSchema(additional=subschema, )
        elif is_dataclass(cls):
            return auto_dataclass(cls)
        else:
            raise NotImplementedError(('class2', cls))
    else:
        if is_optional_type(cls):
            item, _ = get_last_args(cls)

            trc('1').debug('%s %s', item, _)
            r = auto_any(item)

            if isinstance(r, schema.Nullable):
                r.nullable = True
            else:
                raise NotImplementedError('can not be nullable')

            trc('2').debug('%s', r)
            return r
        else:
            raise NotImplementedError(('class', cls))
Пример #19
0
def _get_attr_value(type_: type, indent: int, key: str = "<Unknown>") -> str:
    """
    Indentation is a parameter used for Union and Dict.
    If the type parameter is a "Optional" type, only the args are used (without the NoneType)
    """
    if is_optional_type(type_):
        referred_type = typing_inspect.get_args(type_, evaluate=True)[0]
        type_ = referred_type

    for predicate_function, handle_function in LIST_OF_IMPLEMENTATIONS:
        if predicate_function(type_):
            return handle_function(type_, indent=indent)

    raise RuntimeError(
        f"Alfacase Schema does not know how to handle {type_}.\n"
        f"Perhaps you forgot to add the type hint to attribute named '{key}'?")
Пример #20
0
def is_opt(typ) -> bool:
    """
    Test if the type is `typing.Optional`.

    >>> is_opt(Optional[int])
    True
    >>> is_opt(Optional)
    True
    >>> is_opt(None.__class__)
    False
    """
    args = type_args(typ)
    if args:
        return typing_inspect.is_optional_type(typ) and len(
            args) == 2 and not is_none(args[0]) and is_none(args[1])
    else:
        return typ is Optional
Пример #21
0
def _get_attribute_schema(key: str, value: attr.ib, indent: int = 2) -> str:
    """
    Helper method that return the equivalent schema for the given key and value.

    Keep in mind that for stricyyaml schema, an Optional type means that the user don't need
    to inform a value. While for type hint, an Optional type means that the attribute accepts None.
    """
    if value.default is attr.NOTHING and is_optional_type(value.type):
        msg = (
            "StrictYAML doesn't support None value (only missing keys denote a value), "
            "therefore Optional type are only allowed when the case has a default value."
        )
        raise TypeError(msg)
    attribute_name = _get_attr_name(key, value)
    attribute_value = _get_attr_value(value.type, indent=indent, key=key)

    return f"{INDENTANTION * indent}{attribute_name}: {attribute_value}" + ","
Пример #22
0
 async def _get_dict_value_from_message(
     self, arg: str, dict_prefix: str, dict_with_prefixed_names: Dict[str, Any]
 ) -> Dict[str, Any]:
     value = {k[len(dict_prefix) :]: v for k, v in dict_with_prefixed_names.items()}
     # Check if the values should be converted to lists
     type_args = self._argspec.annotations.get(arg)
     if typing_inspect.is_optional_type(type_args):
         # If optional, get the type args from the not None type argument
         dict_args = typing_inspect.get_args(typing_inspect.get_args(type_args, evaluate=True)[0], evaluate=True)
     else:
         dict_args = typing_inspect.get_args(self._argspec.annotations.get(arg), evaluate=True)
     dict_value_arg_type = (
         typing_inspect.get_origin(dict_args[1]) if typing_inspect.get_origin(dict_args[1]) else dict_args[1]
     )
     if issubclass(dict_value_arg_type, list):
         value = {key: [val] if not isinstance(val, list) else val for key, val in value.items()}
     return value
Пример #23
0
def unwrap_optional(cls: typing.Type) -> typing.Type:
    assert typing_inspect.is_optional_type(cls)
    if not typing_inspect.is_union_type(cls):
        raise NotImplementedError

    origin_args = typing_inspect.get_args(cls)

    selected_tt = None
    for tt in origin_args:
        if tt is type(None):
            continue
        elif selected_tt is None:
            selected_tt = tt
        else:
            raise TypeError

    return selected_tt
Пример #24
0
def _validate_value(value: object, target_type: Type[object]) -> None:
    if target_type is Any:
        return
    elif _is_list(target_type):
        _validate_list(value, cast(Type[List[object]], target_type))
    elif _is_dictionary(target_type):
        _validate_dictionary(value, cast(Type[Dict[object, object]], target_type))
    elif _is_typed_dictionary(target_type):
        _validate_typed_dictionary(value, target_type)
    elif is_optional_type(target_type):
        if value is None:
            return
        _validate_value(value, get_args(target_type)[0])
    else:
        if target_type not in [int, float, str, bool]:
            raise InvalidJson(f"Invalid value type {target_type}")
        if not isinstance(value, target_type):
            raise InvalidJson(f"`{value}` is not a {target_type}")
Пример #25
0
def get_key_and_value_types(dict_type: Type[Dict], Serializable=Serializable) -> Tuple[Optional[Type], Optional[Type]]:
    args = get_type_arguments(dict_type)

    if len(args) != 2:
        logger.debug(f"Weird.. the type {dict_type} doesn't have 2 args: {args}")
        return None, None
    K_ = args[0]
    V_ = args[1]
    # Get rid of Unions or ForwardRefs or Optionals
    V_ = get_actual_type(V_)

    logger.debug(f"K_: {K_}, V_: {V_}")
    if isinstance(V_, tuple):
        V_ = get_first_non_None_type(V_)
    elif tpi.is_optional_type(V_):
        logger.debug(f"V_ is optional: {V_}")
        V_ = get_first_non_None_type(V_)
    return K_, V_
Пример #26
0
def _analyze_signature_set_oas_set(
    request_body: oas.OASRequestBody,
    body_arg: t.Type[t.Any],
) -> t.Tuple[t.Set[exceptions.Error], bool]:
    logger.trace('Operation defines both request body and argument handler')
    is_required = request_body.required
    is_arg_required = not ti.is_optional_type(body_arg)

    if is_required and not is_arg_required:
        return {
            exceptions.Error(
                param_name='body',
                reason=exceptions.IncorrectTypeReason(
                    actual=body_arg,
                    expected=model.BODY_TYPES,
                ),
            ),
        }, True
    return set(), True
Пример #27
0
def get_type_hint_type(type_hint):
    """Given a type hint, return the type, whether it's contained in a List and whether it's Optional."""
    optional = typing_inspect.is_optional_type(type_hint)
    if optional:
        type_hint = typing_inspect.get_args(type_hint)[0]
    origin = typing_inspect.get_origin(type_hint)

    is_list = False

    if origin in (list, List, tuple, Tuple):
        is_list = True
        args = typing_inspect.get_args(type_hint)
        if args and not type(args[0]) == TypeVar:
            type_ = args[0]
        else:
            type_ = origin
    else:
        type_ = type_hint

    return type_, is_list, optional
Пример #28
0
def dump_type(stream: Writer, typ: Type):
    if is_optional_type(typ):
        stream.write('Optional', color=Color.Green)
        stream.write('[')
        dump_type(stream, unpack_type_argument(typ))
        stream.write(']')
    elif is_sequence_type(typ):
        stream.write('Sequence', color=Color.Green)
        stream.write('[')
        dump_type(stream, unpack_type_argument(typ))
        stream.write(']')
    elif is_generic_type(typ):
        stream.write(get_origin(typ).__name__, color=Color.Green)
        stream.write('[')
        for idx, element in enumerate(get_args(typ)):
            if idx:
                stream.write(', ')
            dump_type(stream, element)
        stream.write(']')
    else:
        stream.write(typ.__name__, color=Color.Green)
Пример #29
0
def json_to(type: Type[_V]) -> Callable[[object], _V]:  # pylint: disable=redefined-builtin, too-many-return-statements
    try:
        return type.json_to  # type: ignore
    except AttributeError:
        if is_optional_type(type):  # needs to come before 'issubclass'
            return _json_to_optional(get_args(type)[0])  # type: ignore
        if is_generic_type(type):  # needs to come before 'issubclass'
            if get_origin(type) == list:
                return _json_to_list(get_args(type)[0])  # type: ignore
            if get_origin(type) == dict:
                return _json_to_dict(get_args(type)[1])  # type: ignore
        if issubclass(type, bool):  # needs to come before 'int'
            return _json_to_bool  # type: ignore
        if issubclass(type, int):
            return _json_to_int  # type: ignore
        if issubclass(type, str):
            return _json_to_str  # type: ignore
        if issubclass(type, list):
            raise TypeError('no element type specified for list')
        if issubclass(type, dict):
            raise TypeError('no value type specified for dict')
    raise TypeError('not a JSON value type')
Пример #30
0
def field_to_schema(field):
    """Return the schema (key, value) pair for given dataclass field."""
    metadata = field.metadata or dict()

    if (field.default is not dataclasses.MISSING
            or field.default_factory is not dataclasses.MISSING):
        is_optional = True
    elif typing_inspect.is_optional_type(field.type):
        is_optional = True
    else:
        is_optional = False
    key_kwargs = dict(description=metadata.get('description', None))
    if is_optional:
        key_cls = DataclassSchemaOptional
        if dataclasses.is_dataclass(field.type):
            # for nested dataclass, we pass to the optional subclass
            key_kwargs.update(dataclass_cls=field.type)
        else:
            # regular optional type
            # we also need the spacial subclass of optional
            # to handle more gracefully the default factory
            # of the other fileds
            pass
        key_kwargs.update(default=_get_field_default(field))
    else:
        key_cls = schema.Literal
    schema_key = key_cls(field.name, **key_kwargs)
    # If the schema value was already defined by the user
    predefined_schema_value = metadata.get("schema", None)
    if predefined_schema_value is not None:
        schema_value = predefined_schema_value
    else:
        # Otherwise we infer schema value from the field type
        if dataclasses.is_dataclass(field.type):
            schema_value = getattr(field.type, 'schema', field.type)
        else:
            schema_value = field.type
    return (schema_key, schema_value)