Passed
Push — master ( 9adf86...b4eeac )
by Ramon
01:32
created

jsons.JsonSerializable.load()   A

Complexity

Conditions 1

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 3
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
"""
2
Works with Python3.5+
3
4
JSON (de)serialization (jsons) from and to dicts and plain old Python objects.
5
6
Works with dataclasses (Python3.7+).
7
8
9
**Example:**
10
11
    >>> from dataclasses import dataclass
12
    >>> @dataclass
13
    ... class Car:
14
    ...     color: str
15
    >>> @dataclass
16
    ... class Person:
17
    ...     car: Car
18
    ...     name: str
19
    >>> c = Car('Red')
20
    >>> p = Person(c, 'John')
21
    >>> dumped = dump(p)
22
    >>> dumped['name']
23
    'John'
24
    >>> dumped['car']['color']
25
    'Red'
26
    >>> p_reloaded = load(dumped, Person)
27
    >>> p_reloaded.name
28
    'John'
29
    >>> p_reloaded.car.color
30
    'Red'
31
32
33
Deserialization will work with older Python classes (Python3.5+) given that
34
type hints are present for custom types (i.e. any type that is not set at
35
the bottom of this module). Serialization will work with no type hints at
36
all.
37
38
39
**Example**
40
41
    >>> class Car:
42
    ...     def __init__(self, color):
43
    ...         self.color = color
44
    >>> class Person:
45
    ...     def __init__(self, car: Car, name):
46
    ...         self.car = car
47
    ...         self.name = name
48
    >>> c = Car('Red')
49
    >>> p = Person(c, 'John')
50
    >>> dumped = dump(p)
51
    >>> dumped['name']
52
    'John'
53
    >>> dumped['car']['color']
54
    'Red'
55
    >>> p_reloaded = load(dumped, Person)
56
    >>> p_reloaded.name
57
    'John'
58
    >>> p_reloaded.car.color
59
    'Red'
60
61
62
Alternatively, you can make use of the `JsonSerializable` class.
63
64
65
**Example**
66
67
    >>> class Car(JsonSerializable):
68
    ...     def __init__(self, color):
69
    ...         self.color = color
70
    >>> class Person(JsonSerializable):
71
    ...     def __init__(self, car: Car, name):
72
    ...         self.car = car
73
    ...         self.name = name
74
    >>> c = Car('Red')
75
    >>> p = Person(c, 'John')
76
    >>> dumped = p.json
77
    >>> dumped['name']
78
    'John'
79
    >>> dumped['car']['color']
80
    'Red'
81
    >>> p_reloaded = Person.from_json(dumped)
82
    >>> p_reloaded.name
83
    'John'
84
    >>> p_reloaded.car.color
85
    'Red'
86
87
"""
88
import json
89
from datetime import datetime
90
from enum import Enum
91
from jsons._common_impl import dump_impl, load_impl, CLASSES, SERIALIZERS, \
92
    DESERIALIZERS
93
from jsons.deserializers import default_list_deserializer, \
94
    default_enum_deserializer, default_datetime_deserializer, \
95
    default_string_deserializer, default_primitive_deserializer, \
96
    default_object_deserializer, default_dict_deserializer
97
from jsons.serializers import default_list_serializer, \
98
    default_enum_serializer, default_datetime_serializer, \
99
    default_primitive_serializer, default_object_serializer, \
100
    KEY_TRANSFORMER_SNAKECASE, KEY_TRANSFORMER_CAMELCASE, \
101
    KEY_TRANSFORMER_PASCALCASE, KEY_TRANSFORMER_LISPCASE, \
102
    default_dict_serializer
103
104
dump = dump_impl
0 ignored issues
show
Coding Style Naming introduced by
The name dump does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
105
load = load_impl
0 ignored issues
show
Coding Style Naming introduced by
The name load does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
106
107
108
def dumps(obj: object, *args, **kwargs) -> str:
109
    """
110
    Extend ``json.dumps``, allowing any Python instance to be dumped to a
111
    string. Any extra (keyword) arguments are passed on to ``json.dumps``.
112
113
    :param obj: the object that is to be dumped to a string.
114
    :param args: extra arguments for ``json.dumps``.
115
    :param kwargs: extra keyword arguments for ``json.dumps``. They are also
116
    passed on to the serializer function.
117
    :return: ``obj`` as a ``str``.
118
    """
119
    return json.dumps(dump(obj, **kwargs), *args, **kwargs)
120
121
122
def loads(s: str, cls: type = None, *args, **kwargs) -> object:
0 ignored issues
show
Coding Style Naming introduced by
The name s does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
123
    """
124
    Extend ``json.loads``, allowing a string to be loaded into a dict or a
125
    Python instance of type ``cls``. Any extra (keyword) arguments are passed
126
    on to ``json.loads``.
127
128
    :param s: the string that is to be loaded.
129
    :param cls: a matching class of which an instance should be returned.
130
    :param args: extra arguments for ``json.dumps``.
131
    :param kwargs: extra keyword arguments for ``json.dumps``. They are also
132
    passed on to the deserializer function.
133
    :return: an instance of type ``cls`` or a dict if no ``cls`` is given.
134
    """
135
    obj = json.loads(s, *args, **kwargs)
136
    return load(obj, cls, **kwargs) if cls else obj
137
138
139
def set_serializer(c: callable, cls: type, high_prio: bool = True) -> None:
0 ignored issues
show
Coding Style Naming introduced by
The name c does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
140
    """
141
    Set a serializer function for the given type. You may override the default
142
    behavior of ``jsons.load`` by setting a custom serializer.
143
144
    :param c: the serializer function.
145
    :param cls: the type this serializer can handle.
146
    :param high_prio: determines the order in which is looked for the callable.
147
    :return: None.
148
    """
149
    if cls:
150
        index = 0 if high_prio else len(CLASSES)
151
        CLASSES.insert(index, cls)
152
        SERIALIZERS[cls.__name__] = c
153
    else:
154
        SERIALIZERS['NoneType'] = c
155
156
157
def set_deserializer(c: callable, cls: type, high_prio: bool = True) -> None:
0 ignored issues
show
Coding Style Naming introduced by
The name c does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
158
    """
159
    Set a deserializer function for the given type. You may override the
160
    default behavior of ``jsons.dump`` by setting a custom deserializer.
161
162
    :param c: the deserializer function.
163
    :param cls: the type this serializer can handle.
164
    :param high_prio: determines the order in which is looked for the callable.
165
    :return: None.
166
    """
167
    if cls:
168
        index = 0 if high_prio else len(CLASSES)
169
        CLASSES.insert(index, cls)
170
        DESERIALIZERS[cls.__name__] = c
171
    else:
172
        DESERIALIZERS['NoneType'] = c
173
174
175
class JsonSerializable:
0 ignored issues
show
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
176
    """
177
    This class offers an alternative to using the `jsons.load` and `jsons.dump`
178
    methods. An instance of a class that inherits from JsonSerializable has the
179
    `json` property, which value is equivalent to calling `jsons.dump` on that
180
    instance. Furthermore, you can call `from_json` on that class, which is
181
    equivalent to calling `json.load` with that class as an argument.
182
    """
183
    @property
184
    def json(self) -> dict:
185
        """
186
        See `jsons.dump`.
187
        :return: this instance in a JSON representation (dict).
188
        """
189
        return dump(self)
190
191
    @classmethod
192
    def from_json(cls: type, json_obj: dict, **kwargs) -> object:
193
        """
194
        See `jsons.load`.
195
        :param json_obj: a JSON representation of an instance of the inheriting
196
        class
197
        :param kwargs: the keyword args are passed on to the deserializer
198
        function.
199
        :return: an instance of the inheriting class.
200
        """
201
        return load(json_obj, cls, **kwargs)
202
203
    def dump(self, **kwargs) -> dict:
204
        """
205
        See `jsons.dump`.
206
        :param kwargs: the keyword args are passed on to the serializer
207
        function.
208
        :return: this instance in a JSON representation (dict).
209
        """
210
        return dump(self, **kwargs)
211
212
    @classmethod
213
    def load(cls: type, json_obj: dict, **kwargs) -> object:
214
        """
215
        See `jsons.load`.
216
        :param kwargs: the keyword args are passed on to the serializer
217
        function.
218
        :return: this instance in a JSON representation (dict).
219
        """
220
        return load(json_obj, cls, **kwargs)
221
222
223
set_serializer(default_list_serializer, list)
224
set_serializer(default_dict_serializer, dict)
225
set_serializer(default_enum_serializer, Enum)
226
set_serializer(default_datetime_serializer, datetime)
227
set_serializer(default_primitive_serializer, str)
228
set_serializer(default_primitive_serializer, int)
229
set_serializer(default_primitive_serializer, float)
230
set_serializer(default_primitive_serializer, bool)
231
set_serializer(default_primitive_serializer, None)
232
set_serializer(default_object_serializer, object, False)
233
set_deserializer(default_list_deserializer, list)
234
set_deserializer(default_dict_deserializer, dict)
235
set_deserializer(default_enum_deserializer, Enum)
236
set_deserializer(default_datetime_deserializer, datetime)
237
set_deserializer(default_string_deserializer, str)
238
set_deserializer(default_primitive_deserializer, int)
239
set_deserializer(default_primitive_deserializer, float)
240
set_deserializer(default_primitive_deserializer, bool)
241
set_deserializer(default_primitive_deserializer, None)
242
set_deserializer(default_object_deserializer, object, False)
243