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)
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))
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
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