File: serializable.py

package info (click to toggle)
mautrix-python 0.20.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,812 kB
  • sloc: python: 19,103; makefile: 16
file content (109 lines) | stat: -rw-r--r-- 3,248 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# Copyright (c) 2022 Tulir Asokan
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from typing import Type, TypeVar, Union
from abc import ABC, abstractmethod
from enum import Enum
import json

from ..primitive import JSON

SerializableSubtype = TypeVar("SerializableSubtype", bound="SerializableAttrs")


class Serializable:
    """Serializable is the base class for types with custom JSON serializers."""

    def serialize(self) -> JSON:
        """Convert this object into objects directly serializable with `json`."""
        raise NotImplementedError()

    @classmethod
    def deserialize(cls: Type[SerializableSubtype], raw: JSON) -> SerializableSubtype:
        """Convert the given data parsed from JSON into an object of this type."""
        raise NotImplementedError()

    def json(self) -> str:
        """Serialize this object and dump the output as JSON."""
        return json.dumps(self.serialize())

    @classmethod
    def parse_json(cls: Type[SerializableSubtype], data: Union[str, bytes]) -> SerializableSubtype:
        """Parse the given string as JSON and deserialize the result into this type."""
        return cls.deserialize(json.loads(data))


class SerializerError(Exception):
    """
    SerializerErrors are raised if something goes wrong during serialization or deserialization.
    """

    pass


class UnknownSerializationError(SerializerError):
    def __init__(self) -> None:
        super().__init__("Unknown serialization error")


class AbstractSerializable(ABC, Serializable):
    """
    An abstract Serializable that adds ``@abstractmethod`` decorators.
    """

    @abstractmethod
    def serialize(self) -> JSON:
        pass

    @classmethod
    @abstractmethod
    def deserialize(cls: Type[SerializableSubtype], raw: JSON) -> SerializableSubtype:
        pass


class SerializableEnum(Serializable, Enum):
    """
    A simple Serializable implementation for Enums.

    Examples:
        >>> class MyEnum(SerializableEnum):
        ...     FOO = "foo value"
        ...     BAR = "hmm"
        >>> MyEnum.FOO.serialize()
        "foo value"
        >>> MyEnum.BAR.json()
        '"hmm"'
    """

    def __init__(self, _) -> None:
        """
        A fake ``__init__`` to stop the type checker from complaining.
        Enum's ``__new__`` overrides this.
        """
        super().__init__()

    def serialize(self) -> str:
        """
        Convert this object into objects directly serializable with `json`, i.e. return the value
        set to this enum value.
        """
        return self.value

    @classmethod
    def deserialize(cls: Type[SerializableSubtype], raw: str) -> SerializableSubtype:
        """
        Convert the given data parsed from JSON into an object of this type, i.e. find the enum
        value for the given string using ``cls(raw)``.
        """
        try:
            return cls(raw)
        except ValueError as e:
            raise SerializerError() from e

    def __str__(self):
        return str(self.value)

    def __repr__(self):
        return f"{self.__class__.__name__}.{self.name}"