Passed
Pull Request — master (#16)
by Ramon
51s
created

jsons.serializers._datetime_offset()   A

Complexity

Conditions 4

Size

Total Lines 13
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nop 1
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
8
from enum import EnumMeta
9
from typing import Callable, Optional
10
from jsons import _main_impl
11
from jsons._datetime_impl import get_offset_str
12
from jsons._main_impl import (
13
    RFC3339_DATETIME_PATTERN,
14
    snakecase,
15
    camelcase,
16
    pascalcase,
17
    lispcase,
18
    JsonSerializable
19
)
20
21
22
def default_iterable_serializer(obj: object, **kwargs) -> list:
23
    """
24
    Serialize the given ``obj`` to a list of serialized objects.
25
    :param obj: the iterable that is to be serialized.
26
    :param kwargs: any keyword arguments that may be given to the serialization
27
    process.
28
    :return: a list of which all elements are serialized.
29
    """
30
    return [_main_impl.dump(elem, **kwargs) for elem in obj]
31
32
33
def default_list_serializer(obj: list, **kwargs) -> list:
34
    """
35
    Serialize the given ``obj`` to a list of serialized objects.
36
    :param obj: the list that is to be serialized.
37
    :param kwargs: any keyword arguments that may be given to the serialization
38
    process.
39
    :return: a list of which all elements are serialized.
40
    """
41
    return default_iterable_serializer(obj, **kwargs)
42
43
44
def default_tuple_serializer(obj: tuple, **kwargs) -> list:
45
    """
46
    Serialize the given ``obj`` to a list of serialized objects.
47
    :param obj: the tuple that is to be serialized.
48
    :param kwargs: any keyword arguments that may be given to the serialization
49
    process.
50
    :return: a list of which all elements are serialized.
51
    """
52
    return default_iterable_serializer(obj, **kwargs)
53
54
55
def default_dict_serializer(
56
        obj: dict,
57
        cls: Optional[type] = None,
58
        strip_nulls: bool = False,
59
        key_transformer: Optional[Callable[[str], str]] = None,
60
        **kwargs) -> dict:
61
    """
62
    Serialize the given ``obj`` to a dict of serialized objects.
63
    :param obj: the dict that is to be serialized.
64
    :param key_transformer: a function that will be applied to all keys in the
65
    resulting dict.
66
    :param strip_nulls: if ``True`` the resulting dict will not contain null
67
    values.
68
    :param kwargs: any keyword arguments that may be given to the serialization
69
    process.
70
    :return: a dict of which all elements are serialized.
71
    """
72
    result = dict()
73
    for key in obj:
74
        dumped_elem = _main_impl.dump(obj[key],
75
                                      key_transformer=key_transformer,
76
                                      strip_nulls=strip_nulls, **kwargs)
77
        if not (strip_nulls and dumped_elem is None):
78
            if key_transformer:
79
                key = key_transformer(key)
80
            result[key] = dumped_elem
81
    return result
82
83
84
def default_enum_serializer(obj: EnumMeta,
85
                            use_enum_name: bool = True,
86
                            **_) -> str:
87
    """
88
    Serialize the given obj. By default, the name of the enum element is
89
    returned.
90
    :param obj: an instance of an enum.
91
    :param use_enum_name: determines whether the name or the value should be
92
    used for serialization.
93
    :param _: not used.
94
    :return: ``obj`` serialized as a string.
95
    """
96
    attr = 'name' if use_enum_name else 'value'
97
    return getattr(obj, attr)
98
99
100
def default_datetime_serializer(obj: datetime,
101
                                strip_microseconds: Optional[bool] = True,
102
                                **kwargs) -> str:
103
    """
104
    Serialize the given datetime instance to a string. It uses the RFC3339
105
    pattern. If datetime is a localtime, an offset is provided. If datetime is
106
    in UTC, the result is suffixed with a 'Z'.
107
    :param obj: the datetime instance that is to be serialized.
108
    :param strip_microseconds: determines whether microseconds should be
109
    omitted.
110
    :param kwargs: not used.
111
    :return: ``datetime`` as an RFC3339 string.
112
    """
113
    pattern = RFC3339_DATETIME_PATTERN
114
    offset = get_offset_str(obj)
115
    if not strip_microseconds and obj.microsecond:
116
        pattern += '.%f'
117
    return obj.strftime("{}{}".format(pattern, offset))
118
119
120
def default_primitive_serializer(obj: object, **_) -> object:
121
    """
122
    Serialize a primitive; simply return the given ``obj``.
123
    :param obj: the primitive.
124
    :param _: not used.
125
    :return: ``obj``.
126
    """
127
    return obj
128
129
130
def default_object_serializer(
131
        obj: object,
132
        key_transformer: Optional[Callable[[str], str]] = None,
133
        strip_nulls: bool = False,
134
        strip_privates: bool = False,
135
        strip_properties: bool = False,
136
        strip_class_variables: bool = False,
137
        **kwargs) -> dict:
138
    """
139
    Serialize the given ``obj`` to a dict. All values within ``obj`` are also
140
    serialized. If ``key_transformer`` is given, it will be used to transform
141
    the casing (e.g. snake_case) to a different format (e.g. camelCase).
142
    :param obj: the object that is to be serialized.
143
    :param key_transformer: a function that will be applied to all keys in the
144
    resulting dict.
145
    :param strip_nulls: if ``True`` the resulting dict will not contain null
146
    values.
147
    :param strip_privates: if ``True`` the resulting dict will not contain
148
    private attributes (i.e. attributes that start with an underscore).
149
    :param strip_properties: if ``True`` the resulting dict will not contain
150
    values from @properties.
151
    :param strip_class_variables: if ``True`` the resulting dict will not
152
    contain values from class variables.
153
    :param kwargs: any keyword arguments that are to be passed to the
154
    serializer functions.
155
    :return: a Python dict holding the values of ``obj``.
156
    """
157
    obj_dict = _get_dict_from_obj(obj, strip_privates, strip_properties,
158
                                  strip_class_variables, **kwargs)
159
    return default_dict_serializer(obj_dict,
160
                                   key_transformer=key_transformer,
161
                                   strip_nulls=strip_nulls,
162
                                   strip_privates=strip_privates,
163
                                   strip_properties=strip_properties,
164
                                   strip_class_variables=strip_class_variables,
165
                                   **kwargs)
166
167
168
def _get_dict_from_obj(obj,
169
                       strip_privates,
170
                       strip_properties,
171
                       strip_class_variables,
172
                       cls=None, *_, **__):
173
    excluded_elems = dir(JsonSerializable)
174
    props, other_cls_vars = _get_class_props(obj.__class__)
175
    return {attr: obj.__getattribute__(attr) for attr in dir(obj)
176
            if not attr.startswith('__')
177
            and not (strip_privates and attr.startswith('_'))
178
            and not (strip_properties and attr in props)
179
            and not (strip_class_variables and attr in other_cls_vars)
180
            and attr != 'json'
181
            and not isinstance(obj.__getattribute__(attr), Callable)
182
            and (not cls or attr in cls.__slots__)
183
            and attr not in excluded_elems}
184
185
186
def _get_class_props(cls):
187
    props = []
188
    other_cls_vars = []
189
    for n, v in _get_complete_class_dict(cls).items():
190
        props.append(n) if type(v) is property else other_cls_vars.append(n)
191
    return props, other_cls_vars
192
193
194
def _get_complete_class_dict(cls):
195
    cls_dict = {}
196
    # Loop reversed so values of sub-classes override those of super-classes.
197
    for cls_or_elder in reversed(cls.mro()):
198
        cls_dict.update(cls_or_elder.__dict__)
199
    return cls_dict
200
201
202
# The following default key transformers can be used with the
203
# default_object_serializer and default_dict_serializer.
204
KEY_TRANSFORMER_SNAKECASE = snakecase
205
KEY_TRANSFORMER_CAMELCASE = camelcase
206
KEY_TRANSFORMER_PASCALCASE = pascalcase
207
KEY_TRANSFORMER_LISPCASE = lispcase
208