File size: 2,626 Bytes
550665c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import re
from abc import ABC, abstractmethod
import json

import dateutil.parser


def _type_to_snake_case(type_):
    return re.sub(r'(?<!^)(?=[A-Z])', '_', type_.__name__).lower()


class BaseSerializer(ABC):
    type_ = None

    def __init__(self, obj):
        if isinstance(obj, self.type_):
            self.obj = obj
        elif isinstance(obj, (str, dict)):
            self.obj = self.to_object(obj)
        else:
            raise TypeError('The "{}" object must be {}, str or dict, not {!r}.'
                            .format(_type_to_snake_case(self.type_), self.type_.__name__, obj.__class__.__name__))

    def get_object(self):
        return self.obj

    def get_json(self):
        return self.to_json(self.obj)

    @staticmethod
    def _remove_empty_values(data):
        return {k: v for k, v in data.items() if v is not None}

    @classmethod
    def to_json(cls, obj):
        cls.ensure_type(obj)
        return cls._to_json(obj)

    @staticmethod
    @abstractmethod
    def _to_json(obj):
        pass

    @classmethod
    def to_object(cls, json_):
        json_ = cls.ensure_dict(json_)
        return cls._to_object(json_)

    @staticmethod
    @abstractmethod
    def _to_object(json_):
        pass

    @staticmethod
    def ensure_dict(json_):
        if not isinstance(json_, (str, dict)):
            raise TypeError('The json object must be str or dict, not {!r}'.format(json_.__class__.__name__))

        if isinstance(json_, str):
            return json.loads(json_)
        else:
            return json_

    @classmethod
    def ensure_type(cls, obj):
        if not isinstance(obj, cls.type_):
            raise TypeError('The object must be {}, not {!r}.'.format(cls.type_, obj.__class__.__name__))

    def __init_subclass__(cls, **kwargs):
        """Checks that "type_" is defined and that name of the argument in subclasses __init__ method is the name of

        the "type_" in lowercase. It ensures that error in __init__ function of BaseSerializer has a correct message.

        """
        if cls.type_ is None:
            raise AssertionError('Subclass of BaseSerializer has to define class "type_" that is being serialized.')
        if cls.__init__.__code__.co_varnames != ('self', _type_to_snake_case(cls.type_)):
            raise AssertionError('Argument of the __init__ method has to have a name "{}".'
                                 .format(_type_to_snake_case(cls.type_)))

    @staticmethod
    def _get_datetime_from_string(s):
        return dateutil.parser.parse(s)