Completed
Push — master ( 57e661...05423b )
by Ramon
01:09
created

jsons.serializers   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 84
dl 0
loc 189
rs 10
c 0
b 0
f 0
wmc 23

10 Functions

Rating   Name   Duplication   Size   Complexity  
A default_list_serializer() 0 9 1
A default_tuple_serializer() 0 9 1
A default_enum_serializer() 0 13 2
A default_dict_serializer() 0 24 5
A default_iterable_serializer() 0 9 1
A default_primitive_serializer() 0 8 1
A default_object_serializer() 0 28 2
A _datetime_offset() 0 13 4
A _get_dict_from_obj() 0 9 1
A default_datetime_serializer() 0 25 5
1
"""
2
This module contains default serializers. You can override the serialization
3
process of a particular type as follows:
4
5
``jsons.set_serializer(custom_deserializer, SomeClass)``
6
"""
7
from datetime import datetime, time, timedelta, timezone
8
from enum import EnumMeta
9
from time import gmtime
10
from typing import Callable
11
from jsons import _common_impl
12
from jsons._common_impl import RFC3339_DATETIME_PATTERN, snakecase, \
13
    camelcase, pascalcase, lispcase, JsonSerializable
14
15
16
def default_iterable_serializer(obj, **kwargs) -> list:
17
    """
18
    Serialize the given ``obj`` to a list of serialized objects.
19
    :param obj: the iterable that is to be serialized.
20
    :param kwargs: any keyword arguments that may be given to the serialization
21
    process.
22
    :return: a list of which all elements are serialized.
23
    """
24
    return [_common_impl.dump(elem, **kwargs) for elem in obj]
25
26
27
def default_list_serializer(obj: list, **kwargs) -> list:
28
    """
29
    Serialize the given ``obj`` to a list of serialized objects.
30
    :param obj: the list that is to be serialized.
31
    :param kwargs: any keyword arguments that may be given to the serialization
32
    process.
33
    :return: a list of which all elements are serialized.
34
    """
35
    return default_iterable_serializer(obj, **kwargs)
36
37
38
def default_tuple_serializer(obj: tuple, **kwargs) -> list:
39
    """
40
    Serialize the given ``obj`` to a list of serialized objects.
41
    :param obj: the tuple that is to be serialized.
42
    :param kwargs: any keyword arguments that may be given to the serialization
43
    process.
44
    :return: a list of which all elements are serialized.
45
    """
46
    return default_iterable_serializer(obj, **kwargs)
47
48
49
def default_dict_serializer(obj: dict, strip_nulls: bool = False,
50
                            key_transformer: Callable[[str], str] = None,
51
                            **kwargs) -> dict:
52
    """
53
    Serialize the given ``obj`` to a dict of serialized objects.
54
    :param obj: the dict that is to be serialized.
55
    :param key_transformer: a function that will be applied to all keys in the
56
    resulting dict.
57
    :param strip_nulls: if ``True`` the resulting dict will not contain null
58
    values.
59
    :param kwargs: any keyword arguments that may be given to the serialization
60
    process.
61
    :return: a dict of which all elements are serialized.
62
    """
63
    result = dict()
64
    for key in obj:
65
        dumped_elem = _common_impl.dump(obj[key],
66
                                        key_transformer=key_transformer,
67
                                        strip_nulls=strip_nulls, **kwargs)
68
        if not (strip_nulls and dumped_elem is None):
69
            if key_transformer:
70
                key = key_transformer(key)
71
            result[key] = dumped_elem
72
    return result
73
74
75
def default_enum_serializer(obj: EnumMeta, use_enum_name: bool = True,
76
                            **_) -> str:
77
    """
78
    Serialize the given obj. By default, the name of the enum element is
79
    returned.
80
    :param obj: an instance of an enum.
81
    :param use_enum_name: determines whether the name or the value should be
82
    used for serialization.
83
    :param _: not used.
84
    :return: ``obj`` serialized as a string.
85
    """
86
    attr = 'name' if use_enum_name else 'value'
87
    return getattr(obj, attr)
88
89
90
def default_datetime_serializer(obj: datetime, strip_microseconds: bool = True,
91
                                **_) -> str:
92
    """
93
    Serialize the given datetime instance to a string. It uses the RFC3339
94
    pattern. If datetime is a localtime, an offset is provided. If datetime is
95
    in UTC, the result is suffixed with a 'Z'.
96
    :param obj: the datetime instance that is to be serialized.
97
    :param strip_microseconds: determines whether microseconds should be
98
    omitted.
99
    :param _: not used.
100
    :return: ``datetime`` as an RFC3339 string.
101
    """
102
    pattern = RFC3339_DATETIME_PATTERN
103
    offset = None
104
    tzone = obj.tzinfo
105
    if not tzone:
106
        hrs_delta = datetime.now().hour - gmtime().tm_hour
107
        if hrs_delta == 0:
108
            offset = '+00:00'
109
        else:
110
            tzone = timezone(timedelta(hours=hrs_delta))
111
    offset = offset or _datetime_offset(tzone)
112
    if not strip_microseconds and obj.microsecond:
113
        pattern += '.%f'
114
    return obj.strftime("{}{}".format(pattern, offset))
115
116
117
def _datetime_offset(tzone: timezone) -> str:
118
    offset = 'Z'
119
    if tzone.tzname(None) not in ('UTC', 'UTC+00:00'):
120
        tdelta = tzone.utcoffset(None)
121
        if not tdelta:
122
            tdelta = tzone.adjusted_offset
123
        offset_s = tdelta.total_seconds()
124
        offset_h = int(offset_s / 3600)
125
        offset_m = int((offset_s / 60) % 60)
126
        offset_t = time(abs(offset_h), abs(offset_m))
127
        operator = '+' if offset_s > 0 else '-'
128
        offset = offset_t.strftime('{}%H:%M'.format(operator))
129
    return offset
130
131
132
def default_primitive_serializer(obj, **_) -> object:
133
    """
134
    Serialize a primitive; simply return the given ``obj``.
135
    :param obj:
136
    :param _: not used.
137
    :return: ``obj``.
138
    """
139
    return obj
140
141
142
def default_object_serializer(obj: object,
143
                              key_transformer: Callable[[str], str] = None,
144
                              strip_nulls: bool = False,
145
                              strip_privates: bool = False,
146
                              strip_properties: bool = False,
147
                              **kwargs) -> dict:
148
    """
149
    Serialize the given ``obj`` to a dict. All values within ``obj`` are also
150
    serialized. If ``key_transformer`` is given, it will be used to transform
151
    the casing (e.g. snake_case) to a different format (e.g. camelCase).
152
    :param obj: the object that is to be serialized.
153
    :param key_transformer: a function that will be applied to all keys in the
154
    resulting dict.
155
    :param strip_nulls: if ``True`` the resulting dict will not contain null
156
    values.
157
    :param strip_privates: if ``True`` the resulting dict will not contain
158
    private attributes (i.e. attributes that start with an underscore).
159
    :param strip_properties: if ``True`` the resulting dict will not contain
160
    values from @properties.
161
    :param kwargs: any keyword arguments that are to be passed to the
162
    serializer functions.
163
    :return: a Python dict holding the values of ``obj``.
164
    """
165
    obj_dict = obj.__dict__ if strip_properties and hasattr(obj, '__dict__') \
166
        else _get_dict_from_obj(obj, strip_privates, **kwargs)
167
    return default_dict_serializer(obj_dict, key_transformer=key_transformer,
168
                                   strip_nulls=strip_nulls,
169
                                   strip_privates=strip_privates, **kwargs)
170
171
172
def _get_dict_from_obj(obj, strip_privates, cls=None, *_, **__):
173
    excluded_elems = dir(JsonSerializable)
174
    return {attr: obj.__getattribute__(attr) for attr in dir(obj)
175
            if not attr.startswith('__')
176
            and not (strip_privates and attr.startswith('_'))
177
            and attr != 'json'
178
            and not isinstance(obj.__getattribute__(attr), Callable)
179
            and (not cls or attr in cls.__slots__)
180
            and attr not in excluded_elems}
181
182
183
# The following default key transformers can be used with the
184
# default_object_serializer and default_dict_serializer.
185
KEY_TRANSFORMER_SNAKECASE = snakecase
186
KEY_TRANSFORMER_CAMELCASE = camelcase
187
KEY_TRANSFORMER_PASCALCASE = pascalcase
188
KEY_TRANSFORMER_LISPCASE = lispcase
189