def default_namedtuple_deserializer(obj: Union[list, dict], cls: type,
                                    **kwargs) -> object:
    """
    Deserialize a (JSON) list or dict into a named tuple by deserializing all
    items of that list/dict.
    :param obj: the tuple that needs deserializing.
    :param cls: the NamedTuple.
    :param kwargs: any keyword arguments.
    :return: a deserialized named tuple (i.e. an instance of a class).
    """
    is_dict = isinstance(obj, dict)
    args = []
    for index, field_name in enumerate(cls._fields):
        if index < len(obj):
            key = field_name if is_dict else index
            field = obj[key]
        else:
            field = cls._field_defaults.get(field_name, None)
        if field is None:
            msg = ('No value present in {} for argument "{}"'.format(
                obj, field_name))
            raise UnfulfilledArgumentError(msg, field_name, obj, cls)
        field_types = getattr(cls, '_field_types', None)
        cls_ = field_types.get(field_name) if field_types else None
        loaded_field = load(field, cls_, **kwargs)
        args.append(loaded_field)
    inst = cls(*args)
    return inst
Exemple #2
0
 def load(cls: type, json_obj: object, **kwargs) -> object:
     """
     See ``jsons.load``.
     :param kwargs: the keyword args are passed on to the serializer
     function.
     :param json_obj: the object that is loaded into an instance of `cls`.
     :return: this instance in a JSON representation (dict).
     """
     return load(json_obj, cls, fork_inst=cls, **kwargs)
Exemple #3
0
def default_list_deserializer(obj: list, cls: type = None, **kwargs) -> list:
    """
    Deserialize a list by deserializing all items of that list.
    :param obj: the list that needs deserializing.
    :param cls: the type optionally with a generic (e.g. List[str]).
    :param kwargs: any keyword arguments.
    :return: a deserialized list instance.
    """
    cls_ = None
    if cls and hasattr(cls, '__args__'):
        cls_ = cls.__args__[0]
    return [load(x, cls_, **kwargs) for x in obj]
Exemple #4
0
def _set_remaining_attrs(instance,
                         remaining_attrs,
                         attr_getters=None,
                         **kwargs):
    # Set any remaining attributes on the newly created instance.
    attr_getters = attr_getters or {}
    for attr_name in remaining_attrs:
        loaded_attr = load(remaining_attrs[attr_name],
                           type(remaining_attrs[attr_name]), **kwargs)
        try:
            setattr(instance, attr_name, loaded_attr)
        except AttributeError:
            pass  # This is raised when a @property does not have a setter.
    for attr_name, getter in attr_getters.items():
        setattr(instance, attr_name, getter())
def default_string_deserializer(obj: str,
                                cls: Optional[type] = None,
                                **kwargs) -> object:
    """
    Deserialize a string. If the given ``obj`` can be parsed to a date, a
    ``datetime`` instance is returned.
    :param obj: the string that is to be deserialized.
    :param cls: not used.
    :param kwargs: any keyword arguments.
    :return: the deserialized obj.
    """
    try:
        result = load(obj, datetime, **kwargs)
    except DeserializationError:
        result = obj
    return result
Exemple #6
0
def default_list_deserializer(obj: list, cls: type = None, **kwargs) -> list:
    """
    Deserialize a list by deserializing all items of that list.
    :param obj: the list that needs deserializing.
    :param cls: the type optionally with a generic (e.g. List[str]).
    :param kwargs: any keyword arguments.
    :return: a deserialized list instance.
    """
    cls_ = None
    kwargs_ = {**kwargs}
    if cls and hasattr(cls, '__args__'):
        cls_ = cls.__args__[0]
        # Mark the cls as 'inferred' so that later it is known where cls came
        # from and the precedence of classes can be determined.
        kwargs_['_inferred_cls'] = True
    return [load(x, cls_, **kwargs_) for x in obj]
def _get_value_from_obj(obj, sig, sig_key, meta_hints, **kwargs):
    # Obtain the value for the attribute with the given signature from the
    # given obj. Try to obtain the class of this attribute from the meta info
    # or from type hints.
    cls_key = '/{}'.format(sig_key)
    cls_from_meta = meta_hints.get(cls_key, None)
    new_hints = meta_hints
    arg_cls = None
    if cls_from_meta:
        arg_cls = get_cls_from_str(cls_from_meta, obj, kwargs['fork_inst'])
        # Rebuild the class hints: cls_key becomes the new root.
        new_hints = {
            key.replace(cls_key, '/'): meta_hints[key]
            for key in meta_hints if key != '/'
        }
    elif sig.annotation != inspect.Parameter.empty:
        arg_cls = sig.annotation
    value = load(obj[sig_key], arg_cls, meta_hints=new_hints, **kwargs)
    return value
Exemple #8
0
def default_tuple_deserializer(obj: list,
                               cls: type = None,
                               **kwargs) -> object:
    """
    Deserialize a (JSON) list into a tuple by deserializing all items of that
    list.
    :param obj: the tuple that needs deserializing.
    :param cls: the type optionally with a generic (e.g. Tuple[str, int]).
    :param kwargs: any keyword arguments.
    :return: a deserialized tuple instance.
    """
    if hasattr(cls, '_fields'):
        return default_namedtuple_deserializer(obj, cls, **kwargs)
    tuple_types = getattr(cls, '__tuple_params__', cls.__args__)
    if len(tuple_types) > 1 and tuple_types[1] is ...:
        tuple_types = [tuple_types[0]] * len(obj)
    list_ = [load(value, tuple_types[i], **kwargs)
             for i, value in enumerate(obj)]
    return tuple(list_)
Exemple #9
0
def default_dict_deserializer(obj: dict,
                              cls: type,
                              key_transformer: Optional[Callable[[str],
                                                                 str]] = None,
                              **kwargs) -> dict:
    """
    Deserialize a dict by deserializing all instances of that dict.
    :param obj: the dict that needs deserializing.
    :param key_transformer: a function that transforms the keys to a different
    style (e.g. PascalCase).
    :param cls: not used.
    :param kwargs: any keyword arguments.
    :return: a deserialized dict instance.
    """
    key_transformer = key_transformer or (lambda key: key)
    kwargs_ = {**{'key_transformer': key_transformer}, **kwargs}
    if hasattr(cls, '__args__') and len(cls.__args__) > 1:
        sub_cls = cls.__args__[1]
        kwargs_['cls'] = sub_cls
    return {key_transformer(key): load(obj[key], **kwargs_) for key in obj}
Exemple #10
0
def default_union_deserializer(obj: object, cls: Union, **kwargs) -> object:
    """
    Deserialize an object to any matching type of the given union. The first
    successful deserialization is returned.
    :param obj: The object that needs deserializing.
    :param cls: The Union type with a generic (e.g. Union[str, int]).
    :param kwargs: Any keyword arguments that are passed through the
    deserialization process.
    :return: An object of the first type of the Union that could be
    deserialized successfully.
    """
    for sub_type in cls.__args__:
        try:
            return load(obj, sub_type, **kwargs)
        except JsonsError:
            pass  # Try the next one.
    else:
        args_msg = ', '.join([get_class_name(cls_) for cls_ in cls.__args__])
        err_msg = ('Could not match the object of type "{}" to any type of '
                   'the Union: {}'.format(str(cls), args_msg))
        raise DeserializationError(err_msg, obj, cls)
Exemple #11
0
def _get_value_from_obj(obj, cls, sig, sig_key, meta_hints, **kwargs):
    # Obtain the value for the attribute with the given signature from the
    # given obj. Try to obtain the class of this attribute from the meta info
    # or from type hints.
    cls_key = '/{}'.format(sig_key)
    cls_str_from_meta = meta_hints.get(cls_key, None)
    new_hints = meta_hints
    cls_from_meta = None
    if cls_str_from_meta:
        cls_from_meta = get_cls_from_str(cls_str_from_meta, obj,
                                         kwargs['fork_inst'])
        # Rebuild the class hints: cls_key becomes the new root.
        new_hints = {
            key.replace(cls_key, '/'): meta_hints[key]
            for key in meta_hints if key != '/'
        }
    cls_ = determine_precedence(cls=cls,
                                cls_from_meta=cls_from_meta,
                                cls_from_type=None,
                                inferred_cls=True)
    value = load(obj[sig_key], cls_, meta_hints=new_hints, **kwargs)
    return value
Exemple #12
0
def _get_value_for_attr(obj, cls, sig_key, sig, attr_getters, **kwargs):
    result = None, None
    if obj and sig_key in obj:
        # This argument is in obj.
        arg_cls = None
        if sig.annotation != inspect.Parameter.empty:
            arg_cls = sig.annotation
        value = load(obj[sig_key], arg_cls, **kwargs)
        result = sig_key, value
    elif sig_key in attr_getters:
        # There exists an attr_getter for this argument.
        attr_getter = attr_getters.pop(sig_key)
        result = sig_key, attr_getter()
    elif sig.default != inspect.Parameter.empty:
        # There is a default value for this argument.
        result = sig_key, sig.default
    elif sig.kind not in (inspect.Parameter.VAR_POSITIONAL,
                          inspect.Parameter.VAR_KEYWORD):
        # This argument is no *args or **kwargs and has no value.
        raise UnfulfilledArgumentError(
            'No value found for "{}"'.format(sig_key), sig_key, obj, cls)
    return result
Exemple #13
0
def default_namedtuple_deserializer(obj: list, cls: type, **kwargs) -> object:
    """
    Deserialize a (JSON) list into a named tuple by deserializing all items of
    that list.
    :param obj: the tuple that needs deserializing.
    :param cls: the NamedTuple.
    :param kwargs: any keyword arguments.
    :return: a deserialized named tuple (i.e. an instance of a class).
    """
    args = []
    for index, field_name in enumerate(cls._fields):
        if index < len(obj):
            field = obj[index]
        else:
            field = cls._field_defaults.get(field_name, None)
        if not field:
            msg = ('No value present in {} for argument "{}"'
                   .format(obj, field_name))
            raise UnfulfilledArgumentError(msg, field_name, obj, cls)
        cls_ = cls._field_types.get(field_name, None)
        loaded_field = load(field, cls_, **kwargs)
        args.append(loaded_field)
    inst = cls(*args)
    return inst
Exemple #14
0
 def _wrapper(cls_, inst, **kwargs_):
     return load(inst, cls_, **{**kwargs_, **kwargs})