示例#1
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)
示例#2
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 get_union_params(cls):
        try:
            return load(obj, sub_type, **kwargs)
        except JsonsError:
            pass  # Try the next one.
    else:
        fork_inst = kwargs['fork_inst']
        args_msg = ', '.join([
            get_class_name(cls_, fork_inst=fork_inst)
            for cls_ in get_union_params(cls)
        ])
        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)
示例#3
0
def can_match_with_none(cls: type):
    # Return True if cls allows None; None is a valid value with the given cls.
    result = cls in (Any, object, None, NoneType)
    if not result:
        cls_name = get_class_name(cls).lower()
        result = 'union' in cls_name and NoneType in get_union_params(cls)
    return result
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:
            hint = getattr(cls, '_field_types', {}).get(field_name)
            if type(None) not in (get_union_params(hint) or []):
                # The value 'None' is not permitted here.
                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
示例#5
0
def default_namedtuple_deserializer(obj: Union[list, dict],
                                    cls: type,
                                    *,
                                    key_transformer: Optional[Callable[
                                        [str], str]] = None,
                                    **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)
    key_tfr = key_transformer or (lambda key: key)

    if is_dict:
        tfm_obj = {key_tfr(k): v for k, v in obj.items()}

    args = []
    for index, field_name in enumerate(cls._fields):
        if index < len(obj):
            if is_dict:
                field = tfm_obj[field_name]
            else:
                field = obj[index]
        else:
            field = cls._field_defaults.get(field_name, None)

        # _field_types has been deprecated in favor of __annotations__ in Python 3.8
        if hasattr(cls, '__annotations__'):
            field_types = getattr(cls, '__annotations__', {})
        else:
            field_types = getattr(cls, '_field_types', {})

        if field is None:
            hint = field_types.get(field_name)
            if NoneType not in (get_union_params(hint) or []):
                # The value 'None' is not permitted here.
                msg = ('No value present in {} for argument "{}"'.format(
                    obj, field_name))
                raise UnfulfilledArgumentError(msg, field_name, obj, cls)
        cls_ = field_types.get(field_name) if field_types else None
        loaded_field = load(field,
                            cls_,
                            key_transformer=key_transformer,
                            **kwargs)
        args.append(loaded_field)
    inst = cls(*args)
    return inst