Passed
Pull Request — master (#7)
by
unknown
52s
created

jsons.serializers._get_dict_from_obj()   A

Complexity

Conditions 1

Size

Total Lines 13
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 13
nop 7
dl 0
loc 13
rs 9.75
c 0
b 0
f 0
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
                              strip_class_variables: bool = False,
148
                              **kwargs) -> dict:
149
    """
150
    Serialize the given ``obj`` to a dict. All values within ``obj`` are also
151
    serialized. If ``key_transformer`` is given, it will be used to transform
152
    the casing (e.g. snake_case) to a different format (e.g. camelCase).
153
    :param obj: the object that is to be serialized.
154
    :param key_transformer: a function that will be applied to all keys in the
155
    resulting dict.
156
    :param strip_nulls: if ``True`` the resulting dict will not contain null
157
    values.
158
    :param strip_privates: if ``True`` the resulting dict will not contain
159
    private attributes (i.e. attributes that start with an underscore).
160
    :param strip_properties: if ``True`` the resulting dict will not contain
161
    values from @properties.
162
    :param strip_class_variables: if ``True`` the resulting dict will not
163
    contain values from class variables.
164
    :param kwargs: any keyword arguments that are to be passed to the
165
    serializer functions.
166
    :return: a Python dict holding the values of ``obj``.
167
    """
168
    obj_dict = _get_dict_from_obj(obj, strip_privates, strip_properties,
169
                                  strip_class_variables, **kwargs)
170
    return default_dict_serializer(obj_dict,
171
                                   key_transformer=key_transformer,
172
                                   strip_nulls=strip_nulls,
173
                                   strip_privates=strip_privates,
174
                                   strip_properties=strip_properties,
175
                                   strip_class_variables=strip_class_variables,
176
                                   **kwargs)
177
178
179
def _get_dict_from_obj(obj, strip_privates, strip_properties,
180
                       strip_class_variables, cls=None, *_, **__):
181
    excluded_elems = dir(JsonSerializable)
182
    props, other_cls_vars = _get_class_props(obj.__class__)
183
    return {attr: obj.__getattribute__(attr) for attr in dir(obj)
184
            if not attr.startswith('__')
185
            and not (strip_privates and attr.startswith('_'))
186
            and not (strip_properties and attr in props)
187
            and not (strip_class_variables and attr in other_cls_vars)
188
            and attr != 'json'
189
            and not isinstance(obj.__getattribute__(attr), Callable)
190
            and (not cls or attr in cls.__slots__)
191
            and attr not in excluded_elems}
192
193
194
def _get_class_props(cls):
195
    props = []
196
    other_cls_vars = []
197
    for n, v in _get_complete_class_dict(cls).items():
198
        props.append(n) if type(v) is property else other_cls_vars.append(n)
199
    return props, other_cls_vars
200
201
202
def _get_complete_class_dict(cls):
203
    cls_dict = {}
204
    # Loop reversed so values of sub-classes override those of super-classes.
205
    for cls_or_elder in reversed(cls.mro()):
206
        cls_dict.update(parent_class.__dict__)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable parent_class does not seem to be defined.
Loading history...
207
    return cls_dict
208
209
210
# The following default key transformers can be used with the
211
# default_object_serializer and default_dict_serializer.
212
KEY_TRANSFORMER_SNAKECASE = snakecase
213
KEY_TRANSFORMER_CAMELCASE = camelcase
214
KEY_TRANSFORMER_PASCALCASE = pascalcase
215
KEY_TRANSFORMER_LISPCASE = lispcase
216