looker-sdk 24.16.2__py3-none-any.whl → 24.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. looker_sdk/version.py +1 -1
  2. {looker_sdk-24.16.2.dist-info → looker_sdk-24.18.0.dist-info}/METADATA +1 -1
  3. looker_sdk-24.18.0.dist-info/RECORD +9 -0
  4. {looker_sdk-24.16.2.dist-info → looker_sdk-24.18.0.dist-info}/top_level.txt +0 -1
  5. looker_sdk/rtl/__init__.py +0 -22
  6. looker_sdk/rtl/api_methods.py +0 -247
  7. looker_sdk/rtl/api_settings.py +0 -194
  8. looker_sdk/rtl/auth_session.py +0 -353
  9. looker_sdk/rtl/auth_token.py +0 -101
  10. looker_sdk/rtl/constants.py +0 -31
  11. looker_sdk/rtl/hooks.py +0 -86
  12. looker_sdk/rtl/model.py +0 -230
  13. looker_sdk/rtl/requests_transport.py +0 -110
  14. looker_sdk/rtl/serialize.py +0 -120
  15. looker_sdk/rtl/transport.py +0 -137
  16. looker_sdk/sdk/__init__.py +0 -0
  17. looker_sdk/sdk/api40/__init__.py +0 -1
  18. looker_sdk/sdk/api40/methods.py +0 -13283
  19. looker_sdk/sdk/api40/models.py +0 -15641
  20. looker_sdk/sdk/constants.py +0 -24
  21. looker_sdk-24.16.2.dist-info/RECORD +0 -38
  22. tests/__init__.py +0 -0
  23. tests/conftest.py +0 -133
  24. tests/integration/__init__.py +0 -2
  25. tests/integration/test_methods.py +0 -681
  26. tests/integration/test_netrc.py +0 -55
  27. tests/rtl/__init__.py +0 -2
  28. tests/rtl/test_api_methods.py +0 -216
  29. tests/rtl/test_api_settings.py +0 -252
  30. tests/rtl/test_auth_session.py +0 -284
  31. tests/rtl/test_auth_token.py +0 -70
  32. tests/rtl/test_requests_transport.py +0 -171
  33. tests/rtl/test_serialize.py +0 -770
  34. tests/rtl/test_transport.py +0 -34
  35. {looker_sdk-24.16.2.dist-info → looker_sdk-24.18.0.dist-info}/LICENSE.txt +0 -0
  36. {looker_sdk-24.16.2.dist-info → looker_sdk-24.18.0.dist-info}/WHEEL +0 -0
@@ -1,770 +0,0 @@
1
- # The MIT License (MIT)
2
- #
3
- # Copyright (c) 2019 Looker Data Sciences, Inc.
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- import copy
24
- import datetime
25
- import enum
26
- import functools
27
- import json
28
-
29
- from typing import Optional, Sequence
30
-
31
- try:
32
- from typing import ForwardRef # type: ignore
33
- except ImportError:
34
- from typing import _ForwardRef as ForwardRef # type: ignore
35
-
36
- import attr
37
- import cattr
38
- import pytest # type: ignore
39
-
40
- from looker_sdk.rtl import hooks
41
- from looker_sdk.rtl import model as ml
42
- from looker_sdk.rtl import serialize as sr
43
-
44
-
45
- class Enum1(enum.Enum):
46
- """Predifined enum, used as ForwardRef."""
47
-
48
- entry1 = "entry1"
49
- entry2 = "entry2"
50
- invalid_api_enum_value = "invalid_api_enum_value"
51
-
52
-
53
- # ignore mypy: "Cannot assign to a method"
54
- Enum1.__new__ = ml.safe_enum__new__ # type: ignore
55
-
56
-
57
- @attr.s(auto_attribs=True, init=False)
58
- class ModelNoRefs1(ml.Model):
59
- """Predifined model, used as ForwardRef.
60
-
61
- Since this model has no properties that are forwardrefs to other
62
- objects we can just decorate the class rather than doing the
63
- __annotations__ hack.
64
- """
65
-
66
- name1: str
67
-
68
- def __init__(self, *, name1: str):
69
- self.name1 = name1
70
-
71
-
72
- @attr.s(auto_attribs=True, init=False)
73
- class Model(ml.Model):
74
- """Representative model.
75
-
76
- [De]Serialization of API models relies on the attrs and cattrs
77
- libraries with some additional customization. This model represents
78
- these custom treatments and provides documentation for how and why
79
- they are needed.
80
- """
81
-
82
- # enum1 and model_no_refs1 are both defined before this class
83
- # yet we will still refer to them using forward reference (double quotes)
84
- # because we do not keep track of definition order in the generated code
85
- enum1: "Enum1"
86
- model_no_refs1: "ModelNoRefs1"
87
-
88
- # enum2 and model_no_refs2 are both defined after this class and so the
89
- # forward reference annotation is required.
90
- enum2: "Enum2"
91
- model_no_refs2: "ModelNoRefs2"
92
-
93
- # Optional[] and List[]
94
- list_enum1: Sequence["Enum1"]
95
- list_model_no_refs1: Sequence["ModelNoRefs1"]
96
- opt_enum1: Optional["Enum1"] = None
97
- opt_model_no_refs1: Optional["ModelNoRefs1"] = None
98
- list_opt_model_no_refs1: Optional[Sequence["ModelNoRefs1"]] = None
99
-
100
- # standard types
101
- id: Optional[int] = None
102
- name: Optional[str] = None
103
- datetime_field: Optional[datetime.datetime] = None
104
-
105
- # testing reserved keyword translations
106
- class_: Optional[str] = None
107
- finally_: Optional[Sequence[int]] = None
108
-
109
- # Because this model has "bare" forward ref annotated properties
110
- # (enum1, enum2, model_no_refs1, and model_no_refs2) we need to tell
111
- # the attr library that they're actually ForwardRef objects so that
112
- # cattr will match our forward_ref_structure_hook structure hook
113
- #
114
- #
115
- # Note: just doing the following:
116
- #
117
- # `converter.register_structure_hook("Enum1", structure_hook)`
118
- #
119
- # does not work. cattr stores these hooks using functools singledispatch
120
- # which in turn creates a weakref.WeakKeyDictionary for the dispatch_cache.
121
- # While the registration happens, the cache lookup throws a TypeError
122
- # instead of a KeyError so we never look in the registry.
123
- __annotations__ = {
124
- # python generates these entries as "enum1": "Enum1" etc, we need
125
- # them to be of the form "enum1": ForwardRef("Enum1")
126
- "enum1": ForwardRef("Enum1"),
127
- "model_no_refs1": ForwardRef("ModelNoRefs1"),
128
- "enum2": ForwardRef("Enum2"),
129
- "model_no_refs2": ForwardRef("ModelNoRefs2"),
130
- # python "correctly" inserts the remaining entries but we have to
131
- # define all or nothing using this API
132
- "list_enum1": Sequence["Enum1"],
133
- "list_model_no_refs1": Sequence["ModelNoRefs1"],
134
- "opt_enum1": Optional["Enum1"],
135
- "opt_model_no_refs1": Optional["ModelNoRefs1"],
136
- "list_opt_model_no_refs1": Optional[Sequence["ModelNoRefs1"]],
137
- "id": Optional[int],
138
- "name": Optional[str],
139
- "datetime_field": Optional[datetime.datetime],
140
- "class_": Optional[str],
141
- "finally_": Optional[Sequence[int]],
142
- }
143
-
144
- # store context so that base class can eval "Enum1" instance from
145
- # ForwardRef("Enum1") annotation for __setitem__
146
- __global_context = globals()
147
-
148
- def __init__(
149
- self,
150
- *,
151
- enum1: "Enum1",
152
- model_no_refs1: "ModelNoRefs1",
153
- enum2: "Enum2",
154
- model_no_refs2: "ModelNoRefs2",
155
- list_enum1: Sequence["Enum1"],
156
- list_model_no_refs1: Sequence["ModelNoRefs1"],
157
- opt_enum1: Optional["Enum1"] = None,
158
- opt_model_no_refs1: Optional["ModelNoRefs1"] = None,
159
- list_opt_model_no_refs1: Optional[Sequence["ModelNoRefs1"]] = None,
160
- id: Optional[int] = None,
161
- name: Optional[str] = None,
162
- datetime_field: Optional[datetime.datetime] = None,
163
- class_: Optional[str] = None,
164
- finally_: Optional[Sequence[int]] = None,
165
- ):
166
- """Keep mypy and IDE suggestions happy.
167
-
168
- We cannot use the built in __init__ generation attrs offers
169
- because mypy complains about unknown keyword argument, even
170
- when kw_only=True is set below. Furthermore, IDEs do not pickup
171
- on the attrs generated __init__ so completion suggestion fails
172
- for insantiating these classes otherwise.
173
- """
174
- self.enum1 = enum1
175
- self.model_no_refs1 = model_no_refs1
176
- self.enum2 = enum2
177
- self.model_no_refs2 = model_no_refs2
178
- self.list_enum1 = list_enum1
179
- self.list_model_no_refs1 = list_model_no_refs1
180
- self.opt_enum1 = opt_enum1
181
- self.opt_model_no_refs1 = opt_model_no_refs1
182
- self.list_opt_model_no_refs1 = list_opt_model_no_refs1
183
- self.id = id
184
- self.name = name
185
- self.datetime_field = datetime_field
186
- self.class_ = class_
187
- self.finally_ = finally_
188
-
189
-
190
- class Enum2(enum.Enum):
191
- """Post defined enum, used as ForwardRef."""
192
-
193
- entry2 = "entry2"
194
- invalid_api_enum_value = "invalid_api_enum_value"
195
-
196
-
197
- # ignore mypy: "Cannot assign to a method"
198
- Enum2.__new__ = ml.safe_enum__new__ # type: ignore
199
-
200
-
201
- @attr.s(auto_attribs=True, init=False)
202
- class ModelNoRefs2(ml.Model):
203
- """Post defined model, used as ForwardRef.
204
-
205
- Since this model has no properties that are forwardrefs to other
206
- objects we can just decorate the class rather than doing the
207
- __annotations__ hack.
208
- """
209
-
210
- name2: str
211
-
212
- def __init__(self, *, name2: str):
213
- self.name2 = name2
214
-
215
-
216
- converter = cattr.Converter()
217
- translate_keys_structure_hook = functools.partial(
218
- sr.translate_keys_structure_hook, converter
219
- )
220
- converter.register_structure_hook(Model, translate_keys_structure_hook)
221
- converter.register_structure_hook(datetime.datetime, hooks.datetime_structure_hook)
222
- converter.register_unstructure_hook(datetime.datetime, hooks.datetime_unstructure_hook)
223
- unstructure_hook = functools.partial(hooks.unstructure_hook, converter)
224
- converter.register_unstructure_hook(Model, unstructure_hook)
225
-
226
- # only required for 3.6 for unittest but for some reason integration tests need it
227
- # for all python versions
228
- forward_ref_structure_hook = functools.partial(
229
- sr.forward_ref_structure_hook, globals(), converter
230
- )
231
- converter.register_structure_hook_func(
232
- lambda t: t.__class__ is ForwardRef, forward_ref_structure_hook
233
- )
234
-
235
-
236
- DATETIME_VALUE = datetime.datetime.fromtimestamp(1625246159, datetime.timezone.utc)
237
- DATETIME_VALUE_STR = DATETIME_VALUE.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
238
- MODEL_DATA = {
239
- "enum1": "entry1",
240
- "model_no_refs1": {"name1": "model_no_refs1_name"},
241
- "enum2": "entry2",
242
- "model_no_refs2": {"name2": "model_no_refs2_name"},
243
- "list_enum1": ["entry1"],
244
- "list_model_no_refs1": [{"name1": "model_no_refs1_name"}],
245
- "opt_enum1": "entry1",
246
- "opt_model_no_refs1": {"name1": "model_no_refs1_name"},
247
- "list_opt_model_no_refs1": [{"name1": "model_no_refs1_name"}],
248
- "id": 1,
249
- "name": "my-name",
250
- "datetime_field": DATETIME_VALUE_STR,
251
- "class": "model-name",
252
- "finally": [1, 2, 3],
253
- }
254
-
255
-
256
- @pytest.fixture
257
- def bm():
258
- return Model(
259
- enum1=Enum1.entry1,
260
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
261
- enum2=Enum2.entry2,
262
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
263
- list_enum1=[Enum1.entry1],
264
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
265
- opt_enum1=Enum1.entry1,
266
- opt_model_no_refs1=None,
267
- id=1,
268
- name="my-name",
269
- datetime_field=DATETIME_VALUE,
270
- class_="model-name",
271
- finally_=[1, 2, 3],
272
- )
273
-
274
-
275
- def test_dict_getitem_simple(bm):
276
- assert bm["id"] == bm.id
277
- assert bm["name"] == bm.name
278
- assert bm["class"] == bm.class_
279
- assert bm["finally"] == bm.finally_
280
- with pytest.raises(KeyError):
281
- bm["class_"]
282
- with pytest.raises(KeyError):
283
- bm["finally_"]
284
-
285
-
286
- def test_dict_getitem_child(bm):
287
- assert bm["model_no_refs1"] == ModelNoRefs1(name1="model_no_refs1_name")
288
- assert bm["model_no_refs1"] is bm.model_no_refs1
289
- assert bm["model_no_refs1"]["name1"] == "model_no_refs1_name"
290
- assert bm["list_model_no_refs1"][0] == ModelNoRefs1(name1="model_no_refs1_name")
291
- assert bm["list_model_no_refs1"][0] is bm.list_model_no_refs1[0]
292
- assert bm["list_model_no_refs1"][0]["name1"] == "model_no_refs1_name"
293
- # Model defines this property and `bm.opt_model_no_refs1 is None` so key
294
- # access here should do the same (https://git.io/JRrKm)
295
- assert bm["opt_model_no_refs1"] is None
296
- with pytest.raises(KeyError):
297
- bm["no_such_prop"]
298
-
299
-
300
- def test_dict_getitem_enum(bm):
301
- assert bm["enum1"] == "entry1"
302
-
303
-
304
- def test_dict_setitem_simple(bm):
305
- bm["id"] = 2
306
- assert bm["id"] == 2
307
- assert bm.id == 2
308
- bm["name"] = "some-name"
309
- assert bm["name"] == "some-name"
310
- assert bm.name == "some-name"
311
- bm["class"] = "some-class"
312
- assert bm["class"] == "some-class"
313
- assert bm.class_ == "some-class"
314
- bm["finally"] = [4, 5, 6]
315
- assert bm["finally"] == [4, 5, 6]
316
- assert bm["finally"] is bm.finally_
317
- with pytest.raises(AttributeError) as exc:
318
- bm["foobar"] = 5
319
- assert str(exc.value) == "'Model' object has no attribute 'foobar'"
320
-
321
-
322
- def test_dict_setitem_child(bm):
323
- bm["model_no_refs1"]["name1"] = "model_no_refs1_another_name"
324
- assert bm["model_no_refs1"]["name1"] == "model_no_refs1_another_name"
325
-
326
- # creating new children from dictionaries instantiates a new child model
327
- # object from that dict but does not maintain a reference.
328
- child_dict = {"name1": "I used a dictionary"}
329
- bm["model_no_refs1"] = child_dict
330
- assert bm["model_no_refs1"] == ModelNoRefs1(name1="I used a dictionary")
331
- assert bm.model_no_refs1 is not child_dict
332
- bm["model_no_refs1"]["name1"] = "I'm not a reference to child_dict"
333
- assert child_dict["name1"] != "I'm not a reference to child_dict"
334
- assert child_dict["name1"] == "I used a dictionary"
335
-
336
-
337
- def test_dict_setitem_enum(bm):
338
- bm["enum1"] = "entry2"
339
- assert bm["enum1"] == "entry2"
340
- # it's really still an Enum1 member under the hood
341
- assert bm.enum1 == Enum1.entry2
342
- with pytest.raises(ValueError) as exc:
343
- bm["enum1"] = "foobar"
344
- assert str(exc.value) == (
345
- "Invalid value 'foobar' for 'Model.enum1'. Valid values are ['entry1', 'entry2']"
346
- )
347
- with pytest.raises(ValueError) as exc:
348
- bm["enum1"] = Enum1.entry1 # can't use a real Enum with dict
349
- assert str(exc.value) == (
350
- "Invalid value 'Enum1.entry1' for 'Model.enum1'. Valid values are "
351
- "['entry1', 'entry2']"
352
- )
353
-
354
-
355
- def test_dict_delitem(bm):
356
- del bm["id"]
357
- assert bm.id is None
358
- # Model defines this property and `bm.id is None` so key
359
- # access here should do the same (https://git.io/JRrKm)
360
- assert bm["id"] is None
361
- del bm["class"]
362
- assert bm.class_ is None
363
- # Model defines this property and `bm.class_ is None` so key
364
- # access here should do the same (https://git.io/JRrKm)
365
- assert bm["class"] is None
366
- with pytest.raises(KeyError):
367
- del bm["no-such-key"]
368
-
369
-
370
- def test_dict_iter(bm):
371
- keys = [
372
- "enum1",
373
- "model_no_refs1",
374
- "enum2",
375
- "model_no_refs2",
376
- "list_enum1",
377
- "list_model_no_refs1",
378
- "opt_enum1",
379
- "id",
380
- "name",
381
- "datetime_field",
382
- "class",
383
- "finally",
384
- ]
385
- for k in bm:
386
- keys.remove(k)
387
- assert keys == []
388
-
389
-
390
- def test_dict_contains(bm):
391
- assert "id" in bm
392
- assert "foobar" not in bm
393
- assert "finally_" not in bm
394
- assert "finally" in bm
395
-
396
-
397
- def test_dict_keys(bm):
398
- assert list(bm.keys()) == [
399
- "enum1",
400
- "model_no_refs1",
401
- "enum2",
402
- "model_no_refs2",
403
- "list_enum1",
404
- "list_model_no_refs1",
405
- "opt_enum1",
406
- "id",
407
- "name",
408
- "datetime_field",
409
- "class",
410
- "finally",
411
- ]
412
-
413
-
414
- def test_dict_items(bm):
415
- assert list(bm.items()) == [
416
- ("enum1", "entry1"),
417
- ("model_no_refs1", {"name1": "model_no_refs1_name"}),
418
- ("enum2", "entry2"),
419
- ("model_no_refs2", {"name2": "model_no_refs2_name"}),
420
- ("list_enum1", ["entry1"]),
421
- ("list_model_no_refs1", [{"name1": "model_no_refs1_name"}]),
422
- ("opt_enum1", "entry1"),
423
- ("id", 1),
424
- ("name", "my-name"),
425
- ("datetime_field", DATETIME_VALUE_STR),
426
- ("class", "model-name"),
427
- ("finally", [1, 2, 3]),
428
- ]
429
-
430
-
431
- def test_dict_values(bm):
432
- assert list(bm.values()) == [
433
- "entry1",
434
- {"name1": "model_no_refs1_name"},
435
- "entry2",
436
- {"name2": "model_no_refs2_name"},
437
- ["entry1"],
438
- [{"name1": "model_no_refs1_name"}],
439
- "entry1",
440
- 1,
441
- "my-name",
442
- DATETIME_VALUE_STR,
443
- "model-name",
444
- [1, 2, 3],
445
- ]
446
-
447
-
448
- def test_dict_get(bm):
449
- assert bm.get("id") == bm.id
450
- assert bm.get("id", 5000) == bm.id
451
- assert bm.get("not-a-key") is None
452
- assert bm.get("not-a-key", "default-value") == "default-value"
453
- assert bm.get("model_no_refs1") is bm["model_no_refs1"]
454
- assert bm["model_no_refs1"].get("name1") == "model_no_refs1_name"
455
- assert bm["model_no_refs1"].get("name1", "default-name") == "model_no_refs1_name"
456
- assert bm["model_no_refs1"].get("name2") is None
457
- assert bm["model_no_refs1"].get("name2", "default-name") == "default-name"
458
-
459
-
460
- def test_dict_pop(bm):
461
- assert bm.pop("id") == 1
462
- assert bm.id is None
463
- # Model defines this property and `bm.id is None` so key
464
- # access here should do the same (https://git.io/JRrKm)
465
- assert bm["id"] is None
466
-
467
- assert bm.pop("name", "default-name") == "my-name"
468
- assert bm.name is None
469
- # Model defines this property and `bm.name is None` so key
470
- # access here should do the same (https://git.io/JRrKm)
471
- assert bm["name"] is None
472
-
473
- assert bm.pop("no-name", "default-name") == "default-name"
474
-
475
- assert bm.pop("class") == "model-name"
476
- assert bm.class_ is None
477
- # Model defines this property and `bm.name is None` so key
478
- # access here should do the same (https://git.io/JRrKm)
479
- assert bm["class"] is None
480
-
481
-
482
- def test_dict_popitem(bm):
483
- with pytest.raises(NotImplementedError):
484
- bm.popitem()
485
-
486
-
487
- def test_dict_clear(bm):
488
- with pytest.raises(NotImplementedError):
489
- bm.popitem()
490
-
491
-
492
- def test_dict_update(bm):
493
- bm.update(
494
- {
495
- "id": 2,
496
- "name": "new-name",
497
- "class": "new-class",
498
- "model_no_refs1": {"name1": "new-name1"},
499
- }
500
- )
501
- assert bm["id"] == 2
502
- assert bm["name"] == "new-name"
503
- assert bm["class"] == "new-class"
504
- assert bm["model_no_refs1"]["name1"] == "new-name1"
505
-
506
-
507
- def test_dict_setdefault(bm):
508
- bm.setdefault("id", 2)
509
- assert bm["id"] == 1
510
- del bm["id"]
511
- bm.setdefault("id", 2)
512
- assert bm["id"] == 2
513
-
514
- with pytest.raises(AttributeError):
515
- bm.setdefault("foobar", 5)
516
-
517
-
518
- def test_dict_copy(bm):
519
- with pytest.raises(NotImplementedError):
520
- bm.copy()
521
-
522
-
523
- def test_deserialize_single() -> None:
524
- """Deserialize functionality
525
-
526
- Should handle python reserved keywords as well as attempting to
527
- convert field values to proper type.
528
- """
529
- # check that type conversion happens, str -> int and int -> str in this case
530
- data = copy.deepcopy(MODEL_DATA)
531
- data["id"] = "1"
532
- data["name"] = 25
533
-
534
- d = json.dumps(data)
535
- model = sr.deserialize(data=d, structure=Model, converter=converter)
536
- assert model == Model(
537
- enum1=Enum1.entry1,
538
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
539
- enum2=Enum2.entry2,
540
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
541
- list_enum1=[Enum1.entry1],
542
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
543
- opt_enum1=Enum1.entry1,
544
- opt_model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
545
- list_opt_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
546
- id=1,
547
- name="25",
548
- datetime_field=DATETIME_VALUE,
549
- class_="model-name",
550
- finally_=[1, 2, 3],
551
- )
552
-
553
-
554
- def test_deserialize_list():
555
- # check that type conversion happens
556
- data = [MODEL_DATA]
557
-
558
- models = sr.deserialize(
559
- data=json.dumps(data), structure=Sequence[Model], converter=converter
560
- )
561
- assert models == [
562
- Model(
563
- enum1=Enum1.entry1,
564
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
565
- enum2=Enum2.entry2,
566
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
567
- list_enum1=[Enum1.entry1],
568
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
569
- opt_enum1=Enum1.entry1,
570
- opt_model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
571
- list_opt_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
572
- id=1,
573
- name="my-name",
574
- datetime_field=DATETIME_VALUE,
575
- class_="model-name",
576
- finally_=[1, 2, 3],
577
- ),
578
- ]
579
-
580
-
581
- def test_deserialize_partial():
582
- data = copy.deepcopy(MODEL_DATA)
583
- del data["id"]
584
- del data["opt_enum1"]
585
- del data["opt_model_no_refs1"]
586
-
587
- model = sr.deserialize(data=json.dumps(data), structure=Model, converter=converter)
588
- assert model == Model(
589
- enum1=Enum1.entry1,
590
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
591
- enum2=Enum2.entry2,
592
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
593
- list_enum1=[Enum1.entry1],
594
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
595
- opt_enum1=None,
596
- opt_model_no_refs1=None,
597
- list_opt_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
598
- id=None,
599
- name="my-name",
600
- datetime_field=DATETIME_VALUE,
601
- class_="model-name",
602
- finally_=[1, 2, 3],
603
- )
604
-
605
-
606
- def test_deserialize_with_null():
607
- data = copy.deepcopy(MODEL_DATA)
608
-
609
- # json.dumps sets None to null
610
- data["id"] = None
611
- data["opt_enum1"] = None
612
- data["opt_model_no_refs1"] = None
613
-
614
- model = sr.deserialize(data=json.dumps(data), structure=Model, converter=converter)
615
- assert model == Model(
616
- enum1=Enum1.entry1,
617
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
618
- enum2=Enum2.entry2,
619
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
620
- list_enum1=[Enum1.entry1],
621
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
622
- opt_enum1=None,
623
- opt_model_no_refs1=None,
624
- list_opt_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
625
- id=None,
626
- name="my-name",
627
- datetime_field=DATETIME_VALUE,
628
- class_="model-name",
629
- finally_=[1, 2, 3],
630
- )
631
-
632
-
633
- @pytest.mark.skip(reason="TODO: This breaks CI right now")
634
- @pytest.mark.parametrize(
635
- "data, structure",
636
- [
637
- # ??
638
- # Error: mypy: Variable "tests.rtl.test_serialize.Model" is not valid as a type
639
- (MODEL_DATA, Sequence[Model]), # type: ignore
640
- ([MODEL_DATA], Model),
641
- ],
642
- )
643
- def test_deserialize_data_structure_mismatch(data, structure):
644
- data = json.dumps(data)
645
- with pytest.raises(sr.DeserializeError):
646
- sr.deserialize(data=data, structure=structure, converter=converter)
647
-
648
-
649
- def test_serialize_single():
650
- model = Model(
651
- enum1=Enum1.entry1,
652
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
653
- enum2=Enum2.entry2,
654
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
655
- list_enum1=[Enum1.entry1],
656
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
657
- opt_enum1=Enum1.entry1,
658
- opt_model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
659
- list_opt_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
660
- id=1,
661
- name="my-name",
662
- datetime_field=DATETIME_VALUE,
663
- class_="model-name",
664
- finally_=[1, 2, 3],
665
- )
666
- expected = json.dumps(MODEL_DATA).encode("utf-8")
667
- assert sr.serialize(api_model=model, converter=converter) == expected
668
-
669
-
670
- def test_serialize_sequence():
671
- model = Model(
672
- enum1=Enum1.entry1,
673
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
674
- enum2=Enum2.entry2,
675
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
676
- list_enum1=[Enum1.entry1],
677
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
678
- opt_enum1=Enum1.entry1,
679
- opt_model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
680
- list_opt_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
681
- id=1,
682
- name="my-name",
683
- datetime_field=DATETIME_VALUE,
684
- class_="model-name",
685
- finally_=[1, 2, 3],
686
- )
687
- expected = json.dumps([MODEL_DATA, MODEL_DATA]).encode("utf-8")
688
- assert sr.serialize(api_model=[model, model], converter=converter) == expected
689
-
690
-
691
- def test_serialize_partial():
692
- """Do not send json null for model None field values."""
693
- model = Model(
694
- enum1=Enum1.entry1,
695
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
696
- enum2=Enum2.entry2,
697
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
698
- list_enum1=[Enum1.entry1],
699
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
700
- )
701
- expected = json.dumps(
702
- {
703
- "enum1": "entry1",
704
- "model_no_refs1": {"name1": "model_no_refs1_name"},
705
- "enum2": "entry2",
706
- "model_no_refs2": {"name2": "model_no_refs2_name"},
707
- "list_enum1": ["entry1"],
708
- "list_model_no_refs1": [{"name1": "model_no_refs1_name"}],
709
- }
710
- ).encode("utf-8")
711
- assert sr.serialize(api_model=model, converter=converter) == expected
712
-
713
- @pytest.mark.skip(reason="TODO: This breaks CI right now")
714
- def test_serialize_explict_null():
715
- """Send json null for model field EXPLICIT_NULL values."""
716
- # pass EXPLICIT_NULL into constructor
717
- model = Model(
718
- enum1=Enum1.entry1,
719
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
720
- enum2=Enum2.entry2,
721
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
722
- list_enum1=[Enum1.entry1],
723
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
724
- name=ml.EXPLICIT_NULL,
725
- class_=ml.EXPLICIT_NULL,
726
- )
727
- # set property to EXPLICIT_NULL
728
- model.finally_ = ml.EXPLICIT_NULL
729
-
730
- expected = json.dumps(
731
- {
732
- "enum1": "entry1",
733
- "model_no_refs1": {"name1": "model_no_refs1_name"},
734
- "enum2": "entry2",
735
- "model_no_refs2": {"name2": "model_no_refs2_name"},
736
- "list_enum1": ["entry1"],
737
- "list_model_no_refs1": [{"name1": "model_no_refs1_name"}],
738
- # json.dumps puts these into the json as null
739
- "name": None,
740
- "class": None,
741
- "finally": None,
742
- }
743
- ).encode("utf-8")
744
- assert sr.serialize(api_model=model, converter=converter) == expected
745
-
746
-
747
- def test_safe_enum_deserialization():
748
- data = copy.deepcopy(MODEL_DATA)
749
- data["enum1"] = "not an Enum1 member!"
750
- data["enum2"] = ""
751
- model = Model(
752
- enum1=Enum1.invalid_api_enum_value,
753
- model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
754
- enum2=Enum2.invalid_api_enum_value,
755
- model_no_refs2=ModelNoRefs2(name2="model_no_refs2_name"),
756
- list_enum1=[Enum1.entry1],
757
- list_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
758
- opt_enum1=Enum1.entry1,
759
- opt_model_no_refs1=ModelNoRefs1(name1="model_no_refs1_name"),
760
- list_opt_model_no_refs1=[ModelNoRefs1(name1="model_no_refs1_name")],
761
- id=1,
762
- name="my-name",
763
- datetime_field=DATETIME_VALUE,
764
- class_="model-name",
765
- finally_=[1, 2, 3],
766
- )
767
- assert (
768
- sr.deserialize(data=json.dumps(data), structure=Model, converter=converter)
769
- == model
770
- )