Passed
Branch master (272386)
by Ramon
01:04
created

test_jsons.TestJsons.test_load_object()   A

Complexity

Conditions 1

Size

Total Lines 14
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 14
rs 9.8
c 0
b 0
f 0
cc 1
nop 1
1
from enum import Enum
2
from typing import List, Tuple, Set
3
from unittest.case import TestCase
4
import datetime
5
import jsons
6
import json
7
from jsons import JsonSerializable, KEY_TRANSFORMER_CAMELCASE
8
from jsons._common_impl import snakecase, camelcase, pascalcase, lispcase
9
from jsons.deserializers import KEY_TRANSFORMER_SNAKECASE
10
11
12
class TestJsons(TestCase):
13
14
    def test_dump_str(self):
15
        self.assertEqual('some string', jsons.dump('some string'))
16
17
    def test_dump_int(self):
18
        self.assertEqual(123, jsons.dump(123))
19
20
    def test_dump_float(self):
21
        self.assertEqual(123.456, jsons.dump(123.456))
22
23
    def test_dump_bool(self):
24
        self.assertEqual(True, jsons.dump(True))
25
26
    def test_dump_dict(self):
27
        d = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
28
                              tzinfo=datetime.timezone.utc)
29
        dict_ = {'a': {'b': {'c': {'d': d}}}}
30
        expectation = {'a': {'b': {'c': {'d': '2018-07-08T21:34:00Z'}}}}
31
        self.assertEqual(expectation, jsons.dump(dict_))
32
33
    def test_dump_none(self):
34
        self.assertEqual(None, jsons.dump(None))
35
36
    def test_dump_naive_datetime(self):
37
        d = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34)
38
        self.assertTrue(jsons.dump(d).startswith('2018-07-08T21:34:00'))
39
        self.assertTrue(not jsons.dump(d).endswith('Z'))
40
41
    def test_dump_datetime_with_stripped_microseconds(self):
42
        d = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
43
                              second=10, microsecond=123456,
44
                              tzinfo=datetime.timezone.utc)
45
        dumped = jsons.dump(d)
46
        self.assertEqual('2018-07-08T21:34:10Z', dumped)
47
48
    def test_dump_datetime_with_microseconds(self):
49
        d = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
50
                              microsecond=123456, tzinfo=datetime.timezone.utc)
51
        dumped = jsons.dump(d, strip_microseconds=False)
52
        self.assertEqual('2018-07-08T21:34:00.123456Z', dumped)
53
54
    def test_dump_enum(self):
55
        class E(Enum):
56
            x = 1
57
            y = 2
58
        self.assertEqual('x', jsons.dump(E.x))
59
        self.assertEqual(2, jsons.dump(E.y, use_enum_name=False))
60
61
    def test_dump_list(self):
62
        d = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
63
                              tzinfo=datetime.timezone.utc)
64
        l = [1, 2, 3, [4, 5, [d]]]
65
        self.assertEqual([1, 2, 3, [4, 5, ['2018-07-08T21:34:00Z']]],
66
                         jsons.dump(l))
67
68
    def test_dump_tuple(self):
69
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
70
                                tzinfo=datetime.timezone.utc)
71
        tup = (1, 2, 3, [4, 5, (dat,)])
72
        self.assertEqual([1, 2, 3, [4, 5, ['2018-07-08T21:34:00Z']]],
73
                         jsons.dump(tup))
74
75
    def test_dump_set(self):
76
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
77
                                tzinfo=datetime.timezone.utc)
78
        set_ = {dat, dat}
79
        dumped = jsons.dump(set_)
80
        expected = ['2018-07-08T21:34:00Z']
81
        self.assertEqual(dumped, expected)
82
83
    def test_dump_object(self):
84
        class A:
85
            def __init__(self):
86
                self.name = 'A'
87
88
        class B:
89
            def __init__(self, a):
90
                self.a = a
91
                self.name = 'B'
92
93
        b = B(A())
94
        self.assertEqual({'name': 'B', 'a': {'name': 'A'}}, jsons.dump(b))
95
96
    def test_dump_object_properties(self):
97
        class A:
98
            @property
99
            def x(self):
100
                return 123
101
102
            def y(self):
103
                return 456  # Not to be serialized.
104
105
        a = A()
106
        self.assertEqual({'x': 123}, jsons.dump(a))
107
        self.assertEqual({}, jsons.dump(a, strip_properties=True))
108
109
    def test_dump_object_strip_nulls(self):
110
        class A:
111
            def __init__(self):
112
                self.name = None  # This will be stripped.
113
114
        class B:
115
            def __init__(self, a):
116
                self.a = a
117
                self.name = 'B'
118
119
        b = B(A())
120
        dumped = jsons.dump(b, strip_nulls=True)
121
        self.assertEqual({'name': 'B', 'a': {}}, dumped)
122
123
    def test_dump_object_strip_privates(self):
124
        class A:
125
            def __init__(self):
126
                self._name = 'A'  # This will be stripped.
127
128
        class B:
129
            def __init__(self, a):
130
                self.a = a
131
                self._name = 'B'  # This will be stripped.
132
133
        b = B(A())
134
        dumped = jsons.dump(b, strip_privates=True)
135
        self.assertEqual({'a': {}}, dumped)
136
137
    def test_dump_as_parent_type(self):
138
        class Parent:
139
            __slots__ = ['parent_name']
140
141
            def __init__(self, pname):
142
                self.parent_name = pname
143
144
        class Child(Parent):
145
            def __init__(self, cname, pname):
146
                Parent.__init__(self, pname)
147
                self.child_name = cname
148
149
        c = Child('John', 'William')
150
        dumped1 = jsons.dump(c)
151
        dumped2 = jsons.dump(c, Parent)
152
        self.assertEqual(dumped1, {'child_name': 'John',
153
                                   'parent_name': 'William'})
154
        self.assertEqual(dumped2, {'parent_name': 'William'})
155
156
    def test_load_str(self):
157
        self.assertEqual('some string', jsons.load('some string'))
158
159
    def test_load_int(self):
160
        self.assertEqual(123, jsons.load(123))
161
162
    def test_load_float(self):
163
        self.assertEqual(123.456, jsons.load(123.456))
164
165
    def test_load_bool(self):
166
        self.assertEqual(True, jsons.load(True))
167
168
    def test_load_dict(self):
169
        dumped = {'a': {'b': {'c': {'d': '2018-07-08T21:34:00Z'}}}}
170
        loaded = jsons.load(dumped)
171
        self.assertEqual(loaded['a']['b']['c']['d'].year, 2018)
172
        self.assertEqual(loaded['a']['b']['c']['d'].month, 7)
173
        self.assertEqual(loaded['a']['b']['c']['d'].day, 8)
174
        self.assertEqual(loaded['a']['b']['c']['d'].hour, 21)
175
        self.assertEqual(loaded['a']['b']['c']['d'].minute, 34)
176
        self.assertEqual(loaded['a']['b']['c']['d'].second, 0)
177
178
    def test_load_partially_deserialized_dict(self):
179
        class C:
180
            def __init__(self, d: datetime.datetime):
181
                self.d = d
182
183
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
184
                                tzinfo=datetime.timezone.utc)
185
        dumped = {'d': dat}
186
        loaded = jsons.load(dumped, C)
187
188
        self.assertEqual(loaded.d, dat)
189
190
    def test_load_partially_deserialized_dict_in_strict_mode(self):
191
        class C:
192
            def __init__(self, d: datetime.datetime):
193
                self.d = d
194
195
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
196
                                tzinfo=datetime.timezone.utc)
197
        dumped = {'d': dat}
198
        with self.assertRaises(KeyError):
199
            jsons.load(dumped, C, strict=True)
200
201
    def test_load_none(self):
202
        self.assertEqual(None, jsons.load(None))
203
204
    def test_load_datetime(self):
205
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
206
                                tzinfo=datetime.timezone.utc)
207
        self.assertEqual(dat, jsons.load('2018-07-08T21:34:00Z'))
208
209
    def test_load_enum(self):
210
        class E(Enum):
211
            x = 1
212
            y = 2
213
214
        self.assertEqual(E.x, jsons.load('x', E))
215
        self.assertEqual(E.y, jsons.load(2, E, use_enum_name=False))
216
217
    def test_load_list(self):
218
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
219
                                tzinfo=datetime.timezone.utc)
220
        list_ = [1, 2, 3, [4, 5, [dat]]]
221
        expectation = [1, 2, 3, [4, 5, ['2018-07-08T21:34:00Z']]]
222
        self.assertEqual(list_, jsons.load(expectation))
223
224
    def test_load_tuple(self):
225
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
226
                                tzinfo=datetime.timezone.utc)
227
        tup = (1, ([dat],))
228
        expectation = (1, (['2018-07-08T21:34:00Z'],))
229
        cls = Tuple[int, Tuple[List[datetime.datetime]]]
230
        self.assertEqual(tup, jsons.load(expectation, cls))
231
232
    def test_load_set(self):
233
        dat = datetime.datetime(year=2018, month=7, day=8, hour=21, minute=34,
234
                                tzinfo=datetime.timezone.utc)
235
        loaded1 = jsons.load(['2018-07-08T21:34:00Z'], set)
236
        loaded2 = jsons.load(['2018-07-08T21:34:00Z'], Set[str])
237
        self.assertEqual(loaded1, {dat})
238
        self.assertEqual(loaded2, {'2018-07-08T21:34:00Z'})
239
240
    def test_load_object(self):
241
        class A:
242
            def __init__(self):
243
                self.name = 'A'
244
245
        class B:
246
            def __init__(self, a: A):
247
                self.a = a
248
                self.name = 'B'
249
250
        b = B(A())
251
        loaded_b = jsons.load({'name': 'B', 'a': {'name': 'A'}}, B)
252
        self.assertEqual(b.name, loaded_b.name)
253
        self.assertEqual(b.a.name, loaded_b.a.name)
254
255
    def test_load_object_with_default_value(self):
256
        class A:
257
            def __init__(self, x, y = 2):
258
                self.x = x
259
                self.y = y
260
261
        a = A(1)
262
        loaded_a = jsons.load({'x': 1}, A)
263
        self.assertEqual(a.x, loaded_a.x)
264
        self.assertEqual(a.y, loaded_a.y)
265
266
    def test_dump_load_object_deep(self):
267
        class A:
268
            def __init__(self):
269
                self.name = 'A'
270
271
        class B:
272
            def __init__(self, list_a: List[A],
273
                         list_dates: List[datetime.datetime]):
274
                self.list_a = list_a
275
                self.list_dates = list_dates
276
                self.name = 'B'
277
278
        class C:
279
            def __init__(self, list_b: List[B]):
280
                self.list_b = list_b
281
282
        c = C([B([A(), A()], []),
283
               B([], [datetime.datetime.now(), datetime.datetime.now()])])
284
        dumped_c = jsons.dump(c)
285
        loaded_c = jsons.load(dumped_c, C)
286
        self.assertEqual(loaded_c.list_b[0].name, 'B')
287
        self.assertEqual(loaded_c.list_b[0].list_a[0].name, 'A')
288
        self.assertEqual(loaded_c.list_b[0].list_a[1].name, 'A')
289
        self.assertEqual(loaded_c.list_b[1].list_dates[0].year,
290
                         c.list_b[1].list_dates[0].year)
291
        self.assertEqual(loaded_c.list_b[1].list_dates[0].month,
292
                         c.list_b[1].list_dates[0].month)
293
        self.assertEqual(loaded_c.list_b[1].list_dates[0].day,
294
                         c.list_b[1].list_dates[0].day)
295
        self.assertEqual(loaded_c.list_b[1].list_dates[0].hour,
296
                         c.list_b[1].list_dates[0].hour)
297
        self.assertEqual(loaded_c.list_b[1].list_dates[0].minute,
298
                         c.list_b[1].list_dates[0].minute)
299
        self.assertEqual(loaded_c.list_b[1].list_dates[0].second,
300
                         c.list_b[1].list_dates[0].second)
301
302
    def test_load_object_properties(self):
303
        class WithoutSetter:
304
            @property
305
            def x(self):
306
                return 123
307
308
        class WithSetter:
309
            def __init__(self):
310
                self.__x = 123
311
            @property
312
            def x(self):
313
                return self.__x
314
315
            @x.setter
316
            def x(self, x):
317
                self.__x = x
318
319
        loaded1 = jsons.load({'x': 123}, WithoutSetter)
320
        self.assertEqual(loaded1.x, 123)
321
322
        loaded2 = jsons.load({'x': 456}, WithSetter)
323
        self.assertEqual(loaded2.x, 456)
324
325
    def test_dumps(self):
326
        class A:
327
            def __init__(self):
328
                self.name = 'A'
329
330
        class B:
331
            def __init__(self, a: A):
332
                self.a = a
333
                self.name = 'B'
334
335
        sdumped = jsons.dumps(B(A()))
336
        s = json.dumps({'a': {'name': 'A'}, 'name': 'B'})
337
        self.assertEqual(s, sdumped)
338
339
    def test_loads(self):
340
        class A:
341
            def __init__(self):
342
                self.name = 'A'
343
344
        class B:
345
            def __init__(self, a: A):
346
                self.a = a
347
                self.name = 'B'
348
349
        s = json.dumps({'a': {'name': 'A'}, 'name': 'B'})
350
        loaded_dict = jsons.loads(s)
351
        self.assertEqual('B', loaded_dict['name'])
352
        self.assertEqual('A', loaded_dict['a']['name'])
353
354
        loaded_obj = jsons.loads(s, B)
355
        self.assertEqual('B', loaded_obj.name)
356
        self.assertEqual('A', loaded_obj.a.name)
357
358
    def test_jsonserializable(self):
359
        class Person(JsonSerializable):
360
            def __init__(self, name, age):
361
                self.name = name
362
                self.age = age
363
364
        person = Person('John', 65)
365
        person_json = person.json
366
        person_loaded = Person.from_json(person_json)
367
368
        self.assertEqual(person_json, {'name': 'John', 'age': 65})
369
        self.assertEqual(person.dump(), {'name': 'John', 'age': 65})
370
        self.assertEqual(person_loaded.name, 'John')
371
        self.assertEqual(person_loaded.age, 65)
372
        self.assertEqual(Person.load(person_json).name, 'John')
373
        self.assertEqual(Person.load(person_json).age, 65)
374
375
    def test_jsonserializable_with_kwargs(self):
376
        forked = JsonSerializable\
377
            .with_dump(fork=True, key_transformer=KEY_TRANSFORMER_CAMELCASE)
378
        forked.with_load(key_transformer=KEY_TRANSFORMER_SNAKECASE)
379
380
        class Person(forked):
381
            def __init__(self, my_name):
382
                self.my_name = my_name
383
384
        person = Person('John')
385
        person_json = person.json  # should have camelCase
386
        person_loaded = Person.from_json(person_json)  # should have snake_case
387
388
        self.assertEqual(person_json, {'myName': 'John'})
389
        self.assertEqual(person_loaded.my_name, 'John')
390
        self.assertTrue(isinstance(person_loaded, Person))
391
392
    def test_jsonserializable_fork(self):
393
        f1 = JsonSerializable.fork()
394
        f2 = JsonSerializable.fork()
395
396
        self.assertNotEqual(f1, f2)
397
        self.assertNotEqual(f1.__name__, f2.__name__)
398
399
        f1.set_serializer(lambda *_, **__: 'f1', str)
400
        f2.set_serializer(lambda *_, **__: 'f2', str)
401
402
        class C1(f1):
403
            def __init__(self):
404
                self.x = 'some string'
405
406
        class C2(f2):
407
            def __init__(self):
408
                self.x = 'some string'
409
410
        c1 = C1()
411
        c2 = C2()
412
413
        self.assertEqual(c1.json, {'x': 'f1'})
414
        self.assertEqual(c2.json, {'x': 'f2'})
415
        self.assertEqual(jsons.dump(c1.x), 'some string')
416
        self.assertEqual(jsons.dump(c1.x, fork_inst=f1), 'f1')
417
        self.assertEqual(jsons.dump(c1.x, fork_inst=f2), 'f2')  # Note: c1.x!
418
419
    def test_jsonserializable_fork_of_forks(self):
420
        f1 = JsonSerializable.fork()
421
        f1.set_serializer(lambda *_, **__: 'f1', str)
422
423
        f2 = f1.fork()
424
        f2.set_serializer(lambda *_, **__: 'f2', str)
425
        f2.set_serializer(lambda *_, **__: 999, int)
426
427
        f3 = f2.fork('custom_fork_name')
428
        f3.set_serializer(lambda *_, **__: 'f3', str)
429
430
        self.assertEqual(jsons.dump('some string', fork_inst=f1), 'f1')
431
        self.assertEqual(jsons.dump(123, fork_inst=f1), 123)
432
433
        self.assertEqual(jsons.dump('some string', fork_inst=f2), 'f2')
434
        self.assertEqual(jsons.dump(123, fork_inst=f2), 999)
435
436
        self.assertEqual(jsons.dump('some string', fork_inst=f3), 'f3')
437
        self.assertEqual(jsons.dump(123, fork_inst=f3), 999)
438
        self.assertEqual(f3.__name__, 'custom_fork_name')
439
440
    def test_case_transformers(self):
441
        camelcase_str = 'camelCase'
442
        snakecase_str = 'snake_case'
443
        pascalcase_str = 'Pascal_case'
444
        pascalcase_str2 = 'ABcDe'
445
        lispcase_str = 'lisp-case'
446
447
        self.assertEqual(camelcase(camelcase_str), 'camelCase')
448
        self.assertEqual(camelcase(snakecase_str), 'snakeCase')
449
        self.assertEqual(camelcase(pascalcase_str), 'pascalCase')
450
        self.assertEqual(camelcase(pascalcase_str2), 'aBcDe')
451
        self.assertEqual(camelcase(lispcase_str), 'lispCase')
452
453
        self.assertEqual(snakecase(camelcase_str), 'camel_case')
454
        self.assertEqual(snakecase(snakecase_str), 'snake_case')
455
        self.assertEqual(snakecase(pascalcase_str), 'pascal_case')
456
        self.assertEqual(snakecase(pascalcase_str2), 'a_bc_de')
457
        self.assertEqual(snakecase(lispcase_str), 'lisp_case')
458
459
        self.assertEqual(pascalcase(camelcase_str), 'CamelCase')
460
        self.assertEqual(pascalcase(snakecase_str), 'SnakeCase')
461
        self.assertEqual(pascalcase(pascalcase_str), 'PascalCase')
462
        self.assertEqual(pascalcase(pascalcase_str2), 'ABcDe')
463
        self.assertEqual(pascalcase(lispcase_str), 'LispCase')
464
465
        self.assertEqual(lispcase(camelcase_str), 'camel-case')
466
        self.assertEqual(lispcase(snakecase_str), 'snake-case')
467
        self.assertEqual(lispcase(pascalcase_str), 'pascal-case')
468
        self.assertEqual(lispcase(pascalcase_str2), 'a-bc-de')
469
        self.assertEqual(lispcase(lispcase_str), 'lisp-case')
470
471
    def test_serialize_and_deserialize_with_case_transformer(self):
472
        class A:
473
            def __init__(self, snake_case_str, some_dict):
474
                self.snake_case_str = snake_case_str
475
                self.some_dict = some_dict
476
477
        class B:
478
            def __init__(self, a_obj: A, camel_case_str):
479
                self.a_obj = a_obj
480
                self.camel_case_str = camel_case_str
481
482
        b = B(A('one_two', {'some_key': 'some_value'}), 'theeFour')
483
        dumped_pascalcase = \
484
            jsons.dump(b, key_transformer=jsons.KEY_TRANSFORMER_PASCALCASE)
485
        loaded_snakecase = \
486
            jsons.load(dumped_pascalcase, B,
487
                       key_transformer=jsons.KEY_TRANSFORMER_SNAKECASE)
488
        expected_dump = {
489
            'AObj': {
490
                'SnakeCaseStr': 'one_two',
491
                'SomeDict': {
492
                    'SomeKey': 'some_value'
493
                }
494
            },
495
            'CamelCaseStr': 'theeFour'
496
        }
497
        self.assertEqual(expected_dump, dumped_pascalcase)
498
        self.assertEqual(loaded_snakecase.camel_case_str, 'theeFour')
499
        self.assertEqual(loaded_snakecase.a_obj.snake_case_str, 'one_two')
500
        self.assertEqual(loaded_snakecase.a_obj.some_dict['some_key'],
501
                         'some_value')
502
503
    def test_set_custom_functions(self):
504
        jsons.set_serializer(lambda *_, **__: 'custom_serializer', str)
505
        jsons.set_deserializer(lambda *_, **__: 'custom_deserializer', str)
506
507
        dumped = jsons.dump('serialize me')
508
        loaded = jsons.load(dumped)
509
510
        self.assertEqual(dumped, 'custom_serializer')
511
        self.assertEqual(loaded, 'custom_deserializer')
512