Beispiel #1
0
def dump(obj: object,
         cls: Optional[type] = None,
         fork_inst: Optional[type] = _StateHolder,
         **kwargs) -> object:
    """
    Serialize the given ``obj`` to a JSON equivalent type (e.g. dict, list,
    int, ...).

    The way objects are serialized can be finetuned by setting serializer
    functions for the specific type using ``set_serializer``.

    You can also provide ``cls`` to specify that ``obj`` needs to be serialized
    as if it was of type ``cls`` (meaning to only take into account attributes
    from ``cls``). The type ``cls`` must have a ``__slots__`` defined. Any type
    will do, but in most cases you may want ``cls`` to be a base class of
    ``obj``.
    :param obj: a Python instance of any sort.
    :param cls: if given, ``obj`` will be dumped as if it is of type ``type``.
    :param fork_inst: if given, it uses this fork of ``JsonSerializable``.
    :param kwargs: the keyword args are passed on to the serializer function.
    :return: the serialized obj as a JSON type.
    """
    if cls and not hasattr(cls, '__slots__'):
        raise SerializationError('Invalid type: "{}". Only types that have a '
                                 '__slots__ defined are allowed when '
                                 'providing "cls".'.format(
                                     get_class_name(cls)))
    cls_ = cls or obj.__class__
    serializer = _get_serializer(cls_, fork_inst)
    kwargs_ = {'fork_inst': fork_inst, **kwargs}
    try:
        return serializer(obj, cls=cls, **kwargs_)
    except Exception as err:
        raise SerializationError(str(err))
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)
Beispiel #3
0
def _do_dump(obj, serializer, cls, initial, kwargs):
    try:
        result = serializer(obj, cls=cls, **kwargs)
        if initial:
            clear()
        return result
    except Exception as err:
        clear()
        raise SerializationError(str(err))
Beispiel #4
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()
    is_attrs_cls = getattr(cls, '__attrs_attrs__', None) is not None
    make_attributes_public = is_attrs_cls and not strip_privates
    for attr_name, cls_ in attributes.items():
        attr = getattr(obj, attr_name)
        attr_type = cls_ or type(attr)
        announce_class(attr_type, fork_inst=fork_inst)
        serializer = get_serializer(attr_type, fork_inst)
        try:
            dumped_elem = serializer(
                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 Exception as err:
            if strict:
                raise SerializationError(message=err.args[0]) from err
            else:
                fork_inst._warn(
                    'Failed to dump attribute "{}" of object of '
                    'type "{}". Reason: {}. Ignoring the '
                    'attribute.'.format(attr, get_class_name(cls),
                                        err.args[0]),
                    'attribute-not-serialized')
                break

        if make_attributes_public:
            attr_name = attr_name.lstrip('_')
        _add_dumped_elem(result, attr_name, dumped_elem, strip_nulls,
                         key_transformer)
    return result
Beispiel #5
0
def _get_subclasses(obj: Iterable, cls: type = None) -> Tuple[type, ...]:
    subclasses = (None, ) * len(obj)
    if cls:
        args = get_args(cls)
        if len(args) == 1:
            # E.g. List[int]
            subclasses = args * len(obj)
        elif len(args) > 1:
            # E.g. Tuple[int, str, str]
            subclasses = args
    if len(subclasses) != len(obj):
        msg = ('Not enough generic types ({}) in {}, expected {} to match '
               'the iterable of length {}'.format(len(subclasses), cls,
                                                  len(obj), len(obj)))
        raise SerializationError(msg)
    return subclasses
def default_primitive_serializer(obj: object,
                                 cls: Optional[type] = None,
                                 **kwargs) -> object:
    """
    Serialize a primitive; simply return the given ``obj``.
    :param obj: the primitive.
    :param cls: the type of ``obj``.
    :return: ``obj``.
    """
    result = obj
    if cls and obj is not None and not isinstance(obj, cls):
        try:
            result = cls(obj)
        except ValueError as err:
            raise SerializationError('Could not cast "{}" into "{}"'.format(
                obj, cls.__name__)) from err
    return result