Пример #1
0
def transform(obj: object,
              cls: Type[T],
              *,
              mapper: Callable[[Dict[str, Any]], Dict[str, Any]] = None,
              dump_cls: type = None,
              dump_args: List[Any] = None,
              dump_kwargs: List[Dict[str, Any]] = None,
              **kwargs) -> T:
    """
    Transform the given ``obj`` to an instance of ``cls``.

    :param obj: the object that is to be transformed into a type of ``cls``.
    :param cls: the type that ``obj`` is to be transformed into.
    :param mapper: a callable that takes the dumped dict and returns a mapped
    dict right before it is loaded into ``cls``.
    :param dump_cls: the ``cls`` parameter that is given to ``dump``.
    :param dump_args: the ``args`` parameter that is given to ``dump``.
    :param dump_kwargs: the ``kwargs`` parameter that is given to ``dump``.
    :param kwargs: any keyword arguments that are given to ``load``.
    :return: an instance of ``cls``.
    """
    dump_args_ = dump_args or []
    dump_kwargs_ = dump_kwargs or {}
    dumped = dump(obj, dump_cls, *dump_args_, **dump_kwargs_)
    mapper_ = mapper or (lambda x: x)
    dumped_mapped = mapper_(dumped)
    return load(dumped_mapped, cls, **kwargs)
Пример #2
0
def default_union_serializer(obj: object, cls: Union, **kwargs) -> object:
    """
    Serialize an object to any matching type of the given union. The first
    successful serialization is returned.
    :param obj: The object that is to be serialized.
    :param cls: The Union type with a generic (e.g. Union[str, int]).
    :param kwargs: Any keyword arguments that are passed through the
    serialization process.
    :return: An object of the first type of the Union that could be
    serialized successfully.
    """
    sub_types = get_union_params(cls)

    # Cater for Optional[...]/Union[None, ...] first to avoid blindly
    # string-ifying None in later serializers.
    if obj is None and NoneType in sub_types:
        return obj

    for sub_type in sub_types:
        try:
            return dump(obj, sub_type, **kwargs)
        except JsonsError:
            pass  # Try the next one.
    else:
        args_msg = ', '.join(
            [get_class_name(cls_) for cls_ in get_union_params(cls)])
        err_msg = ('Could not match the object of type "{}" to any type of '
                   'the Union: {}'.format(type(obj), args_msg))
        raise SerializationError(err_msg)
 def dump(self, **kwargs) -> object:
     """
     See ``jsons.dump``.
     :param kwargs: the keyword args are passed on to the serializer
     function.
     :return: this instance in a JSON representation (dict).
     """
     return dump(self, fork_inst=self.__class__, **kwargs)
Пример #4
0
def default_dict_serializer(obj: dict,
                            cls: Optional[type] = None,
                            *,
                            strict: bool = False,
                            strip_nulls: bool = False,
                            key_transformer: Optional[Callable[[str],
                                                               str]] = None,
                            types: Optional[Dict[str, type]] = None,
                            **kwargs) -> dict:
    """
    Serialize the given ``obj`` to a dict of serialized objects.
    :param obj: the dict that is to be serialized.
    :param cls: not used.
    :param strict: if ``True`` the serialization will raise upon any the
    failure of any attribute. Otherwise it continues with a warning.
    :param strict: a bool to determine if the serializer should be strict
    (i.e. only dumping stuff that is known to ``cls``).
    :param strip_nulls: if ``True`` the resulting dict will not contain null
    values.
    :param key_transformer: a function that will be applied to all keys in the
    resulting dict.
    :param types: a ``dict`` with attribute names (keys) and their types
    (values).
    :param kwargs: any keyword arguments that may be given to the serialization
    process.
    :return: a dict of which all elements are serialized.
    """
    result = dict()
    types = types or dict()
    for key in obj:
        obj_ = obj[key]
        cls_ = types.get(key, None)

        # If key is not a valid json type, use the hash as key and store the
        # original key in a separate section.
        dict_and_key = _store_and_hash(result,
                                       key,
                                       key_transformer=key_transformer,
                                       strip_nulls=strip_nulls,
                                       strict=strict,
                                       types=types,
                                       **kwargs)
        if dict_and_key:
            result, key = dict_and_key

        dumped_elem = dump(obj_,
                           cls=cls_,
                           key_transformer=key_transformer,
                           strip_nulls=strip_nulls,
                           strict=strict,
                           **kwargs)
        if not (strip_nulls and dumped_elem is None):
            if key_transformer:
                key = key_transformer(key)
            result[key] = dumped_elem
    return result
Пример #5
0
def default_timezone_serializer(obj: timezone, **kwargs) -> dict:
    """
    Serialize the given timezone instance to a dict holding the total
    seconds.
    :param obj: the timezone instance that is to be serialized.
    :param kwargs: not used.
    :return: ``timezone`` as a dict.
    """
    name = obj.tzname(None)
    offset = dump(obj.utcoffset(None), **kwargs)
    return {'name': name, 'offset': offset}
Пример #6
0
def default_dict_serializer(obj: dict,
                            cls: Optional[type] = None,
                            *,
                            strict: bool = False,
                            strip_nulls: bool = False,
                            key_transformer: Optional[Callable[[str],
                                                               str]] = None,
                            **kwargs) -> dict:
    """
    Serialize the given ``obj`` to a dict of serialized objects.
    :param obj: the dict that is to be serialized.
    :param cls: the type of ``obj``; ``obj`` is dumped as if of that type.
    :param strict: if ``True`` the serialization will raise upon any the
    failure of any attribute. Otherwise it continues with a warning.
    :param strip_nulls: if ``True`` the resulting dict will not contain null
    values.
    :param key_transformer: a function that will be applied to all keys in the
    resulting dict.
    :param kwargs: any keyword arguments that may be given to the serialization
    process.
    :return: a dict of which all elements are serialized.
    """
    result = dict()
    fork_inst = kwargs['fork_inst']
    for key in obj:
        dumped_elem = None
        try:
            dumped_elem = dump(obj[key],
                               key_transformer=key_transformer,
                               strip_nulls=strip_nulls,
                               **kwargs)

            _store_cls_info(dumped_elem, key, obj, **kwargs)

        except RecursionDetectedError:
            fork_inst._warn(
                'Recursive structure detected in attribute "{}" '
                'of object of type "{}", ignoring the attribute.'.format(
                    key, get_class_name(cls)))
        except SerializationError as err:
            if strict:
                raise
            else:
                fork_inst._warn('Failed to dump attribute "{}" of object of '
                                'type "{}". Reason: {}. Ignoring the '
                                'attribute.'.format(key, get_class_name(cls),
                                                    err.message))
                break
        if not (strip_nulls and dumped_elem is None):
            if key_transformer:
                key = key_transformer(key)
            result[key] = dumped_elem
    return result
Пример #7
0
def default_iterable_serializer(obj: Iterable, **kwargs) -> list:
    """
    Serialize the given ``obj`` to a list of serialized objects.
    :param obj: the iterable that is to be serialized.
    :param kwargs: any keyword arguments that may be given to the serialization
    process.
    :return: a list of which all elements are serialized.
    """
    # The meta kwarg store_cls is filtered out, because an iterable should have
    # its own -meta attribute.
    kwargs_ = {key: kwargs[key] for key in kwargs if key != '_store_cls'}
    return [dump(elem, **kwargs_) for elem in obj]
Пример #8
0
def default_namedtuple_serializer(obj: tuple, **kwargs) -> dict:
    """
    Serialize the given ``obj`` to a dict of serialized objects.
    :param obj: the named tuple that is to be serialized.
    :param kwargs: any keyword arguments that may be given to the serialization
    process.
    :return: a dict of which all elements are serialized.
    """
    result = {
        field_name: dump(getattr(obj, field_name), **kwargs)
        for field_name in obj._fields
    }
    return result
Пример #9
0
def _store_and_hash(obj: dict, key: object,
                    **kwargs) -> Optional[Tuple[dict, int]]:
    # Store the given key in the given dict under a special section if that
    # key is not a valid json key. Return a hash of that key.
    result = None
    if not any(issubclass(type(key), json_key) for json_key in JSON_KEYS):
        # Apparently key is not a valid json key value. Since it got into
        # a Python dict without crashing, it must be hashable.
        obj_ = {**obj}
        key_hash = hash(key)
        if '-keys' not in obj_:
            obj_['-keys'] = {}
        dumped_key = dump(key, **kwargs)
        obj_['-keys'][key_hash] = dumped_key
        result = (obj_, key_hash)
    return result
Пример #10
0
def _do_serialize(
        obj: object,
        cls: type,
        attributes: Dict[str, Optional[type]],
        kwargs: dict,
        key_transformer: Optional[Callable[[str], str]] = None,
        strip_nulls: bool = False,
        strip_privates: bool = False,
        strip_properties: bool = False,
        strip_class_variables: bool = False,
        strip_attr: Union[str, MutableSequence[str], Tuple[str]] = None,
        strict: bool = False,
        fork_inst: Optional[type] = StateHolder) -> Dict[str, object]:
    result = dict()
    for attr_name, cls_ in attributes.items():
        attr = obj.__getattribute__(attr_name)
        dumped_elem = None
        try:
            dumped_elem = dump(attr,
                               cls=cls_,
                               key_transformer=key_transformer,
                               strip_nulls=strip_nulls,
                               strip_privates=strip_privates,
                               strip_properties=strip_properties,
                               strip_class_variables=strip_class_variables,
                               strip_attr=strip_attr,
                               **kwargs)
            _store_cls_info(dumped_elem, attr, kwargs)
        except RecursionDetectedError:
            fork_inst._warn(
                'Recursive structure detected in attribute "{}" '
                'of object of type "{}", ignoring the attribute.'.format(
                    attr_name, get_class_name(cls)))
        except SerializationError as err:
            if strict:
                raise
            else:
                fork_inst._warn('Failed to dump attribute "{}" of object of '
                                'type "{}". Reason: {}. Ignoring the '
                                'attribute.'.format(attr, get_class_name(cls),
                                                    err.message))
                break
        _add_dumped_elem(result, attr_name, dumped_elem, strip_nulls,
                         key_transformer)
    return result
Пример #11
0
def _store_and_hash(
        obj: dict,
        key: object,
        **kwargs
) -> Optional[Tuple[dict, int]]:
    # Store the given key in the given dict under a special section if that
    # key is not a valid json key. Return a hash of that key.
    result = None
    if not _is_valid_json_key(key):
        # First try to dump the key, that might be enough already.
        dumped_key = dump(key, **kwargs)
        result = obj, dumped_key
        if not _is_valid_json_key(dumped_key):
            # Apparently, this was not enough; the key is still not "jsonable".
            key_hash = hash(key)
            obj_ = {**obj}
            obj_.setdefault('-keys', {})
            obj_['-keys'][key_hash] = dumped_key
            result = obj_, key_hash
    return result
Пример #12
0
def default_iterable_serializer(obj: Iterable,
                                cls: type = None,
                                *,
                                strict: bool = False,
                                tasks: int = 1,
                                task_type: type = Process,
                                **kwargs) -> list:
    """
    Serialize the given ``obj`` to a list of serialized objects.
    :param obj: the iterable that is to be serialized.
    :param cls: the (subscripted) type of the iterable.
    :param strict: a bool to determine if the serializer should be strict
    (i.e. only dumping stuff that is known to ``cls``).
    :param tasks: the allowed number of tasks (threads or processes).
    :param task_type: the type that is used for multitasking.
    :param kwargs: any keyword arguments that may be given to the serialization
    process.
    :return: a list of which all elements are serialized.
    """
    # The meta kwarg store_cls is filtered out, because an iterable should have
    # its own -meta attribute.
    kwargs_ = {**kwargs, 'strict': strict}
    kwargs_.pop('_store_cls', None)
    if strict:
        cls_ = determine_cls(obj, cls)
        # cls_ = cls or get_type(obj)  # Get the List[T] type from the instance.
        subclasses = _get_subclasses(obj, cls_)
    else:
        subclasses = _get_subclasses(obj, None)

    if tasks < 2:
        result = [
            dump(elem, cls=subclasses[i], **kwargs_)
            for i, elem in enumerate(obj)
        ]
    else:
        zipped_objs = list(zip(obj, subclasses))
        result = multi_task(do_dump, zipped_objs, tasks, task_type, **kwargs_)

    return result
Пример #13
0
def _do_dump(obj_cls_tuple: Tuple[object, type], *args, **kwargs):
    kwargs_ = {**kwargs}
    kwargs_['tasks'] = 1
    return dump(*obj_cls_tuple, *args, **kwargs_)
 def _wrapper(inst, **kwargs_):
     return dump(inst, **{**kwargs_, **kwargs})