예제 #1
0
def test_is_generic_unsupported_python():
    with patch("mashumaro.meta.helpers.PY_36", False):
        with patch("mashumaro.meta.helpers.PY_37", False):
            with patch("mashumaro.meta.helpers.PY_38", False):
                with patch("mashumaro.meta.helpers.PY_39", False):
                    with pytest.raises(NotImplementedError):
                        is_generic(int)
예제 #2
0
    def _unpack_field_value(
            self,
            fname,
            ftype,
            parent,
            value_name="value",
            metadata=MappingProxyType({}),
    ):

        overridden: typing.Optional[str] = None
        deserialize_option = metadata.get("deserialize")
        if deserialize_option is None:
            strategy = metadata.get("serialization_strategy")
            if isinstance(strategy, SerializationStrategy):
                deserialize_option = strategy.deserialize
        if deserialize_option is None:
            strategy = self.get_config().serialization_strategy.get(ftype)
            if isinstance(strategy, dict):
                deserialize_option = strategy.get("deserialize")
            elif isinstance(strategy, SerializationStrategy):
                deserialize_option = strategy.deserialize
        if callable(deserialize_option):
            setattr(self.cls, f"__{fname}_deserialize", deserialize_option)
            overridden = f"cls.__{fname}_deserialize({value_name})"

        if is_dataclass(ftype):
            return overridden or (
                f"{type_name(ftype)}.from_dict({value_name}, "
                f"use_bytes, use_enum, use_datetime)")

        with suppress(TypeError):
            if issubclass(ftype, SerializableType):
                return (overridden
                        or f"{type_name(ftype)}._deserialize({value_name})")

        origin_type = get_type_origin(ftype)
        if is_special_typing_primitive(origin_type):
            if origin_type is typing.Any:
                return overridden or value_name
            elif is_union(ftype):
                args = getattr(ftype, "__args__", ())
                if len(args) == 2 and args[1] == NoneType:  # it is Optional
                    return self._unpack_field_value(fname,
                                                    args[0],
                                                    parent,
                                                    metadata=metadata)
                else:
                    method_name = self._add_unpack_union(
                        fname, ftype, args, parent, metadata)
                    return (f"cls.{method_name}({value_name},"
                            f"use_bytes,use_enum,use_datetime)")
            elif origin_type is typing.AnyStr:
                raise UnserializableDataError(
                    "AnyStr is not supported by mashumaro")
            elif is_type_var(ftype):
                raise UnserializableDataError(
                    "TypeVars are not supported by mashumaro")
            else:
                raise UnserializableDataError(
                    f"{ftype} as a field type is not supported by mashumaro")
        elif origin_type is int:
            return overridden or f"int({value_name})"
        elif origin_type is float:
            return overridden or f"float({value_name})"
        elif origin_type in (bool, NoneType):
            return overridden or value_name
        elif origin_type in (datetime.datetime, datetime.date, datetime.time):
            if overridden:
                return f"{value_name} if use_datetime else {overridden}"
            elif deserialize_option is not None:
                if deserialize_option == "ciso8601":
                    self.ensure_module_imported("ciso8601")
                    datetime_parser = "ciso8601.parse_datetime"
                elif deserialize_option == "pendulum":
                    self.ensure_module_imported("pendulum")
                    datetime_parser = "pendulum.parse"
                else:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        f"Unsupported deserialization engine "
                        f'"{deserialize_option}"',
                    )
                suffix = ""
                if origin_type is datetime.date:
                    suffix = ".date()"
                elif origin_type is datetime.time:
                    suffix = ".time()"
                return (f"{value_name} if use_datetime else "
                        f"{datetime_parser}({value_name}){suffix}")
            return (f"{value_name} if use_datetime else "
                    f"datetime.{origin_type.__name__}."
                    f"fromisoformat({value_name})")
        elif origin_type is datetime.timedelta:
            return overridden or f"datetime.timedelta(seconds={value_name})"
        elif origin_type is datetime.timezone:
            return overridden or f"parse_timezone({value_name})"
        elif origin_type is uuid.UUID:
            return overridden or f"uuid.UUID({value_name})"
        elif origin_type is ipaddress.IPv4Address:
            return overridden or f"ipaddress.IPv4Address({value_name})"
        elif origin_type is ipaddress.IPv6Address:
            return overridden or f"ipaddress.IPv6Address({value_name})"
        elif origin_type is ipaddress.IPv4Network:
            return overridden or f"ipaddress.IPv4Network({value_name})"
        elif origin_type is ipaddress.IPv6Network:
            return overridden or f"ipaddress.IPv6Network({value_name})"
        elif origin_type is ipaddress.IPv4Interface:
            return overridden or f"ipaddress.IPv4Interface({value_name})"
        elif origin_type is ipaddress.IPv6Interface:
            return overridden or f"ipaddress.IPv6Interface({value_name})"
        elif origin_type is Decimal:
            return overridden or f"Decimal({value_name})"
        elif origin_type is Fraction:
            return overridden or f"Fraction({value_name})"
        elif issubclass(origin_type, typing.Collection) and not issubclass(
                origin_type, enum.Enum):
            args = getattr(ftype, "__args__", ())

            def inner_expr(arg_num=0, v_name="value", v_type=None):
                if v_type:
                    return self._unpack_field_value(fname, v_type, parent,
                                                    v_name)
                else:
                    return self._unpack_field_value(fname, args[arg_num],
                                                    parent, v_name)

            if issubclass(origin_type, typing.ByteString):
                if origin_type is bytes:
                    specific = f"decodebytes({value_name}.encode())"
                    return (f"{value_name} if use_bytes else "
                            f"{overridden or specific}")
                elif origin_type is bytearray:
                    if overridden:
                        overridden = (
                            f"bytearray({value_name}) if use_bytes else "
                            f"{overridden}")
                    specific = (f"bytearray({value_name} if use_bytes else "
                                f"decodebytes({value_name}.encode()))")
                    return overridden or specific
            elif issubclass(origin_type, str):
                return overridden or value_name
            elif issubclass(origin_type, typing.List):
                if is_generic(ftype):
                    return (overridden
                            or f"[{inner_expr()} for value in {value_name}]")
                elif ftype is list:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.List[T] instead")
            elif issubclass(origin_type, typing.Deque):
                if is_generic(ftype):
                    return (overridden or f"collections.deque([{inner_expr()} "
                            f"for value in {value_name}])")
                elif ftype is collections.deque:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.Deque[T] instead")
            elif issubclass(origin_type, typing.Tuple):
                if is_generic(ftype):
                    return (
                        overridden or
                        f"tuple([{inner_expr()} for value in {value_name}])")
                elif ftype is tuple:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.Tuple[T] instead")
            elif issubclass(origin_type, typing.FrozenSet):
                if is_generic(ftype):
                    return (overridden or f"frozenset([{inner_expr()} "
                            f"for value in {value_name}])")
                elif ftype is frozenset:
                    raise UnserializableField(
                        fname, ftype, parent,
                        "Use typing.FrozenSet[T] instead")
            elif issubclass(origin_type, typing.AbstractSet):
                if is_generic(ftype):
                    return (overridden or
                            f"set([{inner_expr()} for value in {value_name}])")
                elif ftype is set:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.Set[T] instead")
            elif issubclass(origin_type, typing.ChainMap):
                if ftype is collections.ChainMap:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.ChainMap[KT,VT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "ChainMaps with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden or f"collections.ChainMap("
                                f'*[{{{inner_expr(0,"key")}:{inner_expr(1)} '
                                f"for key, value in m.items()}} "
                                f"for m in {value_name}])")
            elif PY_37_MIN and issubclass(origin_type, typing.OrderedDict):
                if ftype is collections.OrderedDict:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.OrderedDict[KT,VT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "OrderedDict with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden or f"collections.OrderedDict("
                                f'{{{inner_expr(0,"key")}: {inner_expr(1)} '
                                f"for key, value in {value_name}.items()}})")
            elif issubclass(origin_type, typing.Counter):
                if ftype is collections.Counter:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.Counter[KT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "Counter with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden or f"collections.Counter("
                                f'{{{inner_expr(0,"key")}: '
                                f"{inner_expr(1, v_type=int)} "
                                f"for key, value in {value_name}.items()}})")
            elif issubclass(origin_type, typing.Mapping):
                if ftype is dict:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.Dict[KT,VT] or Mapping[KT,VT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "Mappings with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden
                                or f'{{{inner_expr(0,"key")}: {inner_expr(1)} '
                                f"for key, value in {value_name}.items()}}")
            elif issubclass(origin_type, typing.Sequence):
                if is_generic(ftype):
                    return (overridden
                            or f"[{inner_expr()} for value in {value_name}]")
        elif issubclass(origin_type, os.PathLike):
            if overridden:
                return overridden
            elif issubclass(origin_type, pathlib.PosixPath):
                return f"pathlib.PosixPath({value_name})"
            elif issubclass(origin_type, pathlib.WindowsPath):
                return f"pathlib.WindowsPath({value_name})"
            elif issubclass(origin_type, pathlib.Path):
                return f"pathlib.Path({value_name})"
            elif issubclass(origin_type, pathlib.PurePosixPath):
                return f"pathlib.PurePosixPath({value_name})"
            elif issubclass(origin_type, pathlib.PureWindowsPath):
                return f"pathlib.PureWindowsPath({value_name})"
            elif issubclass(origin_type, pathlib.PurePath):
                return f"pathlib.PurePath({value_name})"
            elif origin_type is os.PathLike:
                return f"pathlib.PurePath({value_name})"
            else:
                return f"{type_name(origin_type)}({value_name})"
        elif issubclass(origin_type, enum.Enum):
            specific = f"{type_name(origin_type)}({value_name})"
            return f"{value_name} if use_enum else {overridden or specific}"
        elif overridden:
            return overridden

        raise UnserializableField(fname, ftype, parent)
예제 #3
0
    def _pack_value(
            self,
            fname,
            ftype,
            parent,
            value_name="value",
            metadata=MappingProxyType({}),
    ):

        overridden: typing.Optional[str] = None
        serialize_option = metadata.get("serialize")
        if serialize_option is None:
            strategy = metadata.get("serialization_strategy")
            if isinstance(strategy, SerializationStrategy):
                serialize_option = strategy.serialize
        if serialize_option is None:
            strategy = self.get_config().serialization_strategy.get(ftype)
            if isinstance(strategy, dict):
                serialize_option = strategy.get("serialize")
            elif isinstance(strategy, SerializationStrategy):
                serialize_option = strategy.serialize
        if callable(serialize_option):
            setattr(
                self.cls,
                f"__{fname}_serialize",
                staticmethod(serialize_option),
            )
            overridden = f"self.__{fname}_serialize({value_name})"

        if is_dataclass(ftype):
            flags = self.get_to_dict_flags(ftype)
            return overridden or f"{value_name}.to_dict({flags})"

        with suppress(TypeError):
            if issubclass(ftype, SerializableType):
                return overridden or f"{value_name}._serialize()"

        origin_type = get_type_origin(ftype)
        if is_special_typing_primitive(origin_type):
            if origin_type is typing.Any:
                return overridden or value_name
            elif is_union(ftype):
                args = getattr(ftype, "__args__", ())
                if len(args) == 2 and args[1] == NoneType:  # it is Optional
                    return self._pack_value(fname,
                                            args[0],
                                            parent,
                                            metadata=metadata)
                else:
                    method_name = self._add_pack_union(fname, ftype, args,
                                                       parent, metadata)
                    return (f"self.{method_name}({value_name},"
                            f"{self.get_to_dict_flags()})")
            elif origin_type is typing.AnyStr:
                raise UnserializableDataError(
                    "AnyStr is not supported by mashumaro")
            elif is_type_var(ftype):
                raise UnserializableDataError(
                    "TypeVars are not supported by mashumaro")
            else:
                raise UnserializableDataError(
                    f"{ftype} as a field type is not supported by mashumaro")
        elif origin_type is int:
            return overridden or f"int({value_name})"
        elif origin_type is float:
            return overridden or f"float({value_name})"
        elif origin_type in (bool, NoneType):
            return overridden or value_name
        elif origin_type in (datetime.datetime, datetime.date, datetime.time):
            if overridden:
                return f"{value_name} if use_datetime else {overridden}"
            return (
                f"{value_name} if use_datetime else {value_name}.isoformat()")
        elif origin_type is datetime.timedelta:
            return overridden or f"{value_name}.total_seconds()"
        elif origin_type is datetime.timezone:
            return overridden or f"{value_name}.tzname(None)"
        elif origin_type is uuid.UUID:
            return overridden or f"str({value_name})"
        elif origin_type in [
                ipaddress.IPv4Address,
                ipaddress.IPv6Address,
                ipaddress.IPv4Network,
                ipaddress.IPv6Network,
                ipaddress.IPv4Interface,
                ipaddress.IPv6Interface,
        ]:
            return overridden or f"str({value_name})"
        elif origin_type is Decimal:
            return overridden or f"str({value_name})"
        elif origin_type is Fraction:
            return overridden or f"str({value_name})"
        elif issubclass(origin_type, typing.Collection) and not issubclass(
                origin_type, enum.Enum):
            args = getattr(ftype, "__args__", ())

            def inner_expr(arg_num=0, v_name="value", v_type=None):
                if v_type:
                    return self._pack_value(fname, v_type, parent, v_name)
                else:
                    return self._pack_value(fname, args[arg_num], parent,
                                            v_name)

            if issubclass(origin_type, typing.ByteString):
                specific = f"encodebytes({value_name}).decode()"
                return (
                    f"{value_name} if use_bytes else {overridden or specific}")
            elif issubclass(origin_type, str):
                return overridden or value_name
            elif issubclass(
                    origin_type,
                (typing.List, typing.Deque, typing.Tuple, typing.AbstractSet),
            ):
                if is_generic(ftype):
                    return (overridden
                            or f"[{inner_expr()} for value in {value_name}]")
                elif ftype is list:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.List[T] instead")
                elif ftype is collections.deque:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.Deque[T] instead")
                elif ftype is tuple:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.Tuple[T] instead")
                elif ftype is set:
                    raise UnserializableField(fname, ftype, parent,
                                              "Use typing.Set[T] instead")
                elif ftype is frozenset:
                    raise UnserializableField(
                        fname, ftype, parent,
                        "Use typing.FrozenSet[T] instead")
            elif issubclass(origin_type, typing.ChainMap):
                if ftype is collections.ChainMap:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.ChainMap[KT,VT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "ChainMaps with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden
                                or f'[{{{inner_expr(0,"key")}:{inner_expr(1)} '
                                f"for key,value in m.items()}} "
                                f"for m in value.maps]")
            elif PY_37_MIN and issubclass(origin_type, typing.OrderedDict):
                if ftype is collections.OrderedDict:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.OrderedDict[KT,VT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "OrderedDict with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden or
                                f'{{{inner_expr(0, "key")}: {inner_expr(1)} '
                                f"for key, value in {value_name}.items()}}")
            elif issubclass(origin_type, typing.Counter):
                if ftype is collections.Counter:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.Counter[KT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "Counter with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden or f'{{{inner_expr(0, "key")}: '
                                f"{inner_expr(1, v_type=int)} "
                                f"for key, value in {value_name}.items()}}")
            elif issubclass(origin_type, typing.Mapping):
                if ftype is dict:
                    raise UnserializableField(
                        fname,
                        ftype,
                        parent,
                        "Use typing.Dict[KT,VT] or Mapping[KT,VT] instead",
                    )
                elif is_generic(ftype):
                    if is_dataclass(args[0]):
                        raise UnserializableDataError(
                            "Mappings with dataclasses as keys "
                            "are not supported by mashumaro")
                    else:
                        return (overridden
                                or f'{{{inner_expr(0,"key")}: {inner_expr(1)} '
                                f"for key, value in {value_name}.items()}}")
            elif issubclass(origin_type, typing.Sequence):
                if is_generic(ftype):
                    return (overridden
                            or f"[{inner_expr()} for value in {value_name}]")
        elif issubclass(origin_type, os.PathLike):
            return overridden or f"{value_name}.__fspath__()"
        elif issubclass(origin_type, enum.Enum):
            specific = f"{value_name}.value"
            return f"{value_name} if use_enum else {overridden or specific}"
        elif overridden:
            return overridden

        raise UnserializableField(fname, ftype, parent)