iker-python-common 1.0.9__tar.gz → 1.0.10__tar.gz

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 (66) hide show
  1. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/PKG-INFO +1 -1
  2. iker_python_common-1.0.10/src/iker/common/utils/jsonutils.py +152 -0
  3. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker_python_common.egg-info/PKG-INFO +1 -1
  4. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker_python_common.egg-info/SOURCES.txt +2 -0
  5. iker_python_common-1.0.10/test/iker_tests/common/utils/jsonutils_test.py +504 -0
  6. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/.editorconfig +0 -0
  7. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/.github/workflows/pr.yml +0 -0
  8. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/.github/workflows/push.yml +0 -0
  9. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/.gitignore +0 -0
  10. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/MANIFEST.in +0 -0
  11. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/README.md +0 -0
  12. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/VERSION +0 -0
  13. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/pyproject.toml +0 -0
  14. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
  15. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
  16. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
  17. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  18. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  19. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  20. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  21. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.foo/file.bar +0 -0
  22. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.foo/file.baz +0 -0
  23. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/resources/unittest/shutils/dir.foo/file.foo +0 -0
  24. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/setup.cfg +0 -0
  25. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/setup.py +0 -0
  26. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/__init__.py +0 -0
  27. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/__init__.py +0 -0
  28. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/argutils.py +0 -0
  29. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/config.py +0 -0
  30. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/dbutils.py +0 -0
  31. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/dockerutils.py +0 -0
  32. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/dtutils.py +0 -0
  33. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/funcutils.py +0 -0
  34. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/logger.py +0 -0
  35. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/numutils.py +0 -0
  36. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/randutils.py +0 -0
  37. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/retry.py +0 -0
  38. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/s3utils.py +0 -0
  39. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/sequtils.py +0 -0
  40. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/shutils.py +0 -0
  41. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/span.py +0 -0
  42. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/strutils.py +0 -0
  43. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker/common/utils/testutils.py +0 -0
  44. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  45. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  46. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker_python_common.egg-info/requires.txt +0 -0
  47. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/src/iker_python_common.egg-info/top_level.txt +0 -0
  48. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/__init__.py +0 -0
  49. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/argutils_test.py +0 -0
  50. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/config_test.py +0 -0
  51. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/dbutils_test.py +0 -0
  52. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/dockerutils_test.py +0 -0
  53. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  54. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/funcutils_test.py +0 -0
  55. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/logger_test.py +0 -0
  56. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/numutils_test.py +0 -0
  57. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/randutils_test.py +0 -0
  58. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/retry_test.py +0 -0
  59. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/s3utils_test.py +0 -0
  60. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/sequtils_test.py +0 -0
  61. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/shutils_test.py +0 -0
  62. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/span_test.py +0 -0
  63. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/strutils_test.py +0 -0
  64. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/common/utils/testutils_test.py +0 -0
  65. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/docker_fixtures.py +0 -0
  66. {iker_python_common-1.0.9 → iker_python_common-1.0.10}/test/iker_tests/iker_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iker-python-common
3
- Version: 1.0.9
3
+ Version: 1.0.10
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -0,0 +1,152 @@
1
+ import math
2
+ from collections.abc import Callable, Mapping, Sequence, Set
3
+ from typing import Any, SupportsFloat, SupportsInt
4
+
5
+ from iker.common.utils.numutils import is_normal_real
6
+
7
+ __all__ = [
8
+ "JsonKey",
9
+ "JsonValue",
10
+ "JsonArray",
11
+ "JsonObject",
12
+ "JsonType",
13
+ "JsonKeyCompatible",
14
+ "JsonValueCompatible",
15
+ "JsonArrayCompatible",
16
+ "JsonObjectCompatible",
17
+ "JsonTypeCompatible",
18
+ "json_reformat",
19
+ "json_sanitize",
20
+ "json_compare",
21
+ ]
22
+
23
+ JsonKey = str
24
+ JsonValue = str | bool | float | int | None
25
+ JsonObject = dict[JsonKey, "JsonType"]
26
+ JsonArray = list["JsonType"]
27
+ JsonType = JsonValue | JsonObject | JsonArray
28
+
29
+ JsonKeyCompatible = str
30
+ JsonValueCompatible = str | bool | SupportsFloat | SupportsInt | None
31
+ JsonObjectCompatible = Mapping[JsonKeyCompatible, "JsonTypeCompatible"]
32
+ JsonArrayCompatible = Sequence["JsonTypeCompatible"]
33
+ JsonTypeCompatible = JsonValueCompatible | JsonObjectCompatible | JsonArrayCompatible
34
+
35
+
36
+ def default_key_formatter(key: JsonKeyCompatible) -> JsonKey:
37
+ return str(key)
38
+
39
+
40
+ def default_value_formatter(value: JsonValueCompatible) -> JsonValue:
41
+ if value is None or isinstance(value, JsonValue):
42
+ return value
43
+ if isinstance(value, SupportsFloat):
44
+ return float(value)
45
+ if isinstance(value, SupportsInt):
46
+ return int(value)
47
+ raise ValueError(f"unsupported type '{type(value)}' encountered")
48
+
49
+
50
+ def json_reformat(
51
+ obj: Any,
52
+ key_formatter: Callable[[JsonKeyCompatible], JsonKey] = default_key_formatter,
53
+ value_formatter: Callable[[JsonValueCompatible], JsonType] = default_value_formatter,
54
+ *,
55
+ raise_if_unregistered: bool = True,
56
+ unregistered_formatter: Callable[[Any], JsonType] | None = None,
57
+ ) -> JsonType:
58
+ if obj is None or isinstance(obj, (JsonValue | JsonValueCompatible)):
59
+ return value_formatter(obj)
60
+ if isinstance(obj, Mapping):
61
+ return {key_formatter(key): json_reformat(value,
62
+ key_formatter,
63
+ value_formatter,
64
+ raise_if_unregistered=raise_if_unregistered,
65
+ unregistered_formatter=unregistered_formatter)
66
+ for key, value in obj.items()}
67
+ if isinstance(obj, Sequence):
68
+ return [json_reformat(item,
69
+ key_formatter,
70
+ value_formatter,
71
+ raise_if_unregistered=raise_if_unregistered,
72
+ unregistered_formatter=unregistered_formatter)
73
+ for item in obj]
74
+ if raise_if_unregistered or unregistered_formatter is None:
75
+ raise ValueError(f"unregistered type '{type(obj)}' encountered")
76
+ return unregistered_formatter(obj)
77
+
78
+
79
+ def json_sanitize(obj: Any, *, str_inf_nan: bool = True, str_unregistered: bool = True) -> JsonType:
80
+ def value_formatter(value: JsonValue) -> JsonValue:
81
+ if isinstance(value, float) and not is_normal_real(value):
82
+ return str(value) if str_inf_nan else None
83
+ return default_value_formatter(value)
84
+
85
+ def unregistered_formatter(unregistered: Any) -> JsonType:
86
+ if isinstance(unregistered, Set):
87
+ return [json_sanitize(item, str_inf_nan=str_inf_nan, str_unregistered=str_unregistered)
88
+ for item in unregistered]
89
+ return str(unregistered) if str_unregistered else None
90
+
91
+ return json_reformat(obj,
92
+ value_formatter=value_formatter,
93
+ raise_if_unregistered=False,
94
+ unregistered_formatter=unregistered_formatter)
95
+
96
+
97
+ def json_compare(
98
+ a: JsonType,
99
+ b: JsonType,
100
+ *,
101
+ int_strict: bool = False,
102
+ float_tol: float = 1e-5,
103
+ list_order: bool = True,
104
+ dict_extra: bool = False,
105
+ ) -> bool:
106
+ if a is None or b is None:
107
+ return a is None and b is None
108
+ if isinstance(a, (dict, list, str, bool)):
109
+ if type(a) != type(b):
110
+ return False
111
+ if isinstance(a, dict):
112
+ if not dict_extra and set(a.keys()) != set(b.keys()):
113
+ return False
114
+ return all(json_compare(a[k],
115
+ b[k],
116
+ int_strict=int_strict,
117
+ float_tol=float_tol,
118
+ list_order=list_order,
119
+ dict_extra=dict_extra)
120
+ for k in set(a.keys()) & set(b.keys()))
121
+ if isinstance(a, list):
122
+ if len(a) != len(b):
123
+ return False
124
+ if list_order:
125
+ return all(json_compare(va,
126
+ vb,
127
+ int_strict=int_strict,
128
+ float_tol=float_tol,
129
+ list_order=list_order,
130
+ dict_extra=dict_extra)
131
+ for va, vb in zip(a, b))
132
+ else:
133
+ return all(json_compare(va,
134
+ vb,
135
+ int_strict=int_strict,
136
+ float_tol=float_tol,
137
+ list_order=list_order,
138
+ dict_extra=dict_extra)
139
+ for va, vb in zip(sorted(a), sorted(b)))
140
+ return a == b
141
+ if isinstance(a, (float, int)) and isinstance(b, (float, int)):
142
+ if int_strict:
143
+ if type(a) != type(b):
144
+ return False
145
+ if isinstance(a, float) or isinstance(b, float):
146
+ if math.isnan(a) and math.isnan(b):
147
+ return True
148
+ if math.isinf(a) and math.isinf(b):
149
+ return a == b
150
+ return abs(a - b) <= float_tol
151
+ return a == b
152
+ raise ValueError(f"incompatible type '{type(a)}' and '{type(b)}'")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iker-python-common
3
- Version: 1.0.9
3
+ Version: 1.0.10
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -25,6 +25,7 @@ src/iker/common/utils/dbutils.py
25
25
  src/iker/common/utils/dockerutils.py
26
26
  src/iker/common/utils/dtutils.py
27
27
  src/iker/common/utils/funcutils.py
28
+ src/iker/common/utils/jsonutils.py
28
29
  src/iker/common/utils/logger.py
29
30
  src/iker/common/utils/numutils.py
30
31
  src/iker/common/utils/randutils.py
@@ -50,6 +51,7 @@ test/iker_tests/common/utils/dbutils_test.py
50
51
  test/iker_tests/common/utils/dockerutils_test.py
51
52
  test/iker_tests/common/utils/dtutils_test.py
52
53
  test/iker_tests/common/utils/funcutils_test.py
54
+ test/iker_tests/common/utils/jsonutils_test.py
53
55
  test/iker_tests/common/utils/logger_test.py
54
56
  test/iker_tests/common/utils/numutils_test.py
55
57
  test/iker_tests/common/utils/randutils_test.py
@@ -0,0 +1,504 @@
1
+ import datetime
2
+ import math
3
+ import unittest
4
+ from collections.abc import Mapping, Sequence
5
+ from dataclasses import dataclass
6
+ from typing import SupportsFloat, SupportsInt
7
+
8
+ import ddt
9
+ import pytest
10
+
11
+ from iker.common.utils.jsonutils import json_compare, json_reformat, json_sanitize
12
+
13
+
14
+ @dataclass(eq=True, frozen=True)
15
+ class PrefixedStr(object):
16
+ prefix: str
17
+ value: str
18
+
19
+ def __str__(self):
20
+ return self.prefix + "::" + self.value
21
+
22
+
23
+ @dataclass(eq=True, frozen=True)
24
+ class MultipliedInt(SupportsInt):
25
+ value: int
26
+ multiplier: int = 1
27
+
28
+ def __int__(self):
29
+ return self.value * self.multiplier
30
+
31
+
32
+ @dataclass(eq=True, frozen=True)
33
+ class MultipliedFloat(SupportsFloat):
34
+ value: float
35
+ multiplier: float = 1
36
+
37
+ def __float__(self):
38
+ return self.value * self.multiplier
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class WrappedList(Sequence):
43
+ value: list
44
+
45
+ def __len__(self):
46
+ return len(self.value)
47
+
48
+ def __getitem__(self, index):
49
+ return self.value[index]
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class WrappedDict(Mapping):
54
+ value: dict
55
+
56
+ def __iter__(self):
57
+ return iter(self.value)
58
+
59
+ def __len__(self):
60
+ return len(self.value)
61
+
62
+ def __getitem__(self, index):
63
+ return self.value[index]
64
+
65
+
66
+ @ddt.ddt
67
+ class JsonUtilsTest(unittest.TestCase):
68
+
69
+ @ddt.data(
70
+ (None, None),
71
+ (True, True),
72
+ (False, False),
73
+ (1, 1),
74
+ (-1, -1),
75
+ (0, 0),
76
+ (1.0, 1.0),
77
+ (-1.0, -1.0),
78
+ (0.0, 0.0),
79
+ (1.0e9, 1.0e9),
80
+ (-1.0e-9, -1.0e-9),
81
+ (math.nan, math.nan),
82
+ (math.inf, math.inf),
83
+ (-math.inf, -math.inf),
84
+ (MultipliedInt(-1, 100), -100),
85
+ (MultipliedInt(1, 100), 100),
86
+ (MultipliedFloat(1.0, 100.0), 100.0),
87
+ (MultipliedFloat(-1.0, 100.0), -100.0),
88
+ ("", ""),
89
+ ("dummy", "dummy"),
90
+ (
91
+ [None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, math.nan, math.inf, -math.inf, "", "dummy"],
92
+ [None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, math.nan, math.inf, -math.inf, "", "dummy"],
93
+ ),
94
+ (
95
+ (None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, math.nan, math.inf, -math.inf, "", "dummy"),
96
+ [None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, math.nan, math.inf, -math.inf, "", "dummy"],
97
+ ),
98
+ (
99
+ {
100
+ "none": None,
101
+ "bool_true": True,
102
+ "bool_false": False,
103
+ "int_one": 1,
104
+ "int_minus_one": -1,
105
+ "int_zero": 0,
106
+ "int_wrapped": MultipliedInt(1, -1000),
107
+ "float_one": 1.0,
108
+ "float_minus_one": -1.0,
109
+ "float_zero": 0.0,
110
+ "float_one_e_nine": 1.0e9,
111
+ "float_minus_one_e_minus_nine": -1.0e-9,
112
+ "float_nan": math.nan,
113
+ "float_inf": math.inf,
114
+ "float_minus_inf": -math.inf,
115
+ "float_wrapped": MultipliedFloat(1.0, -1000.0),
116
+ "str_empty": "",
117
+ "str": "dummy",
118
+ },
119
+ {
120
+ "none": None,
121
+ "bool_true": True,
122
+ "bool_false": False,
123
+ "int_one": 1,
124
+ "int_minus_one": -1,
125
+ "int_zero": 0,
126
+ "int_wrapped": -1000,
127
+ "float_one": 1.0,
128
+ "float_minus_one": -1.0,
129
+ "float_zero": 0.0,
130
+ "float_one_e_nine": 1.0e9,
131
+ "float_minus_one_e_minus_nine": -1.0e-9,
132
+ "float_nan": math.nan,
133
+ "float_inf": math.inf,
134
+ "float_minus_inf": -math.inf,
135
+ "float_wrapped": -1000.0,
136
+ "str_empty": "",
137
+ "str": "dummy",
138
+ },
139
+ ),
140
+ (
141
+ {
142
+ "none": None,
143
+ "bool": {
144
+ "bool_true": True,
145
+ "bool_false": False,
146
+ },
147
+ "int": {
148
+ "int_one": 1,
149
+ "int_minus_one": -1,
150
+ "int_zero": 0,
151
+ "int_wrapped": MultipliedInt(1, -1000),
152
+ },
153
+ "float": {
154
+ "float_one": 1.0,
155
+ "float_minus_one": -1.0,
156
+ "float_zero": 0.0,
157
+ "float_one_e_nine": 1.0e9,
158
+ "float_minus_one_e_minus_nine": -1.0e-9,
159
+ "float_nan": math.nan,
160
+ "float_inf": math.inf,
161
+ "float_minus_inf": -math.inf,
162
+ "float_wrapped": MultipliedFloat(1.0, -1000.0),
163
+ },
164
+ "str": {
165
+ "str_empty": "",
166
+ "str": "dummy",
167
+ },
168
+ "list":
169
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"],
170
+ "tuple":
171
+ (None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"),
172
+ },
173
+ {
174
+ "none": None,
175
+ "bool": {
176
+ "bool_true": True,
177
+ "bool_false": False,
178
+ },
179
+ "int": {
180
+ "int_one": 1,
181
+ "int_minus_one": -1,
182
+ "int_zero": 0,
183
+ "int_wrapped": -1000,
184
+ },
185
+ "float": {
186
+ "float_one": 1.0,
187
+ "float_minus_one": -1.0,
188
+ "float_zero": 0.0,
189
+ "float_one_e_nine": 1.0e9,
190
+ "float_minus_one_e_minus_nine": -1.0e-9,
191
+ "float_nan": math.nan,
192
+ "float_inf": math.inf,
193
+ "float_minus_inf": -math.inf,
194
+ "float_wrapped": -1000.0,
195
+ },
196
+ "str": {
197
+ "str_empty": "",
198
+ "str": "dummy",
199
+ },
200
+ "list":
201
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"],
202
+ "tuple":
203
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"],
204
+ },
205
+ ),
206
+ (
207
+ WrappedDict({
208
+ PrefixedStr("key", "none"): None,
209
+ PrefixedStr("key", "bool"): WrappedDict({
210
+ PrefixedStr("key", "bool_true"): True,
211
+ PrefixedStr("key", "bool_false"): False,
212
+ }),
213
+ PrefixedStr("key", "int"): WrappedDict({
214
+ PrefixedStr("key", "int_one"): 1,
215
+ PrefixedStr("key", "int_minus_one"): -1,
216
+ PrefixedStr("key", "int_zero"): 0,
217
+ PrefixedStr("key", "int_wrapped"): MultipliedInt(1, -1000),
218
+ }),
219
+ PrefixedStr("key", "float"): WrappedDict({
220
+ PrefixedStr("key", "float_one"): 1.0,
221
+ PrefixedStr("key", "float_minus_one"): -1.0,
222
+ PrefixedStr("key", "float_zero"): 0.0,
223
+ PrefixedStr("key", "float_one_e_nine"): 1.0e9,
224
+ PrefixedStr("key", "float_minus_one_e_minus_nine"): -1.0e-9,
225
+ PrefixedStr("key", "float_nan"): math.nan,
226
+ PrefixedStr("key", "float_inf"): math.inf,
227
+ PrefixedStr("key", "float_minus_inf"): -math.inf,
228
+ PrefixedStr("key", "float_wrapped"): MultipliedFloat(1.0, -1000.0),
229
+ }),
230
+ PrefixedStr("key", "str"): WrappedDict({
231
+ PrefixedStr("key", "str_empty"): "",
232
+ PrefixedStr("key", "str"): "dummy",
233
+ }),
234
+ PrefixedStr("key", "list"): WrappedList(
235
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"]),
236
+ PrefixedStr("key", "tuple"):
237
+ (None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"),
238
+ }),
239
+ {
240
+ "key::none": None,
241
+ "key::bool": {
242
+ "key::bool_true": True,
243
+ "key::bool_false": False,
244
+ },
245
+ "key::int": {
246
+ "key::int_one": 1,
247
+ "key::int_minus_one": -1,
248
+ "key::int_zero": 0,
249
+ "key::int_wrapped": -1000,
250
+ },
251
+ "key::float": {
252
+ "key::float_one": 1.0,
253
+ "key::float_minus_one": -1.0,
254
+ "key::float_zero": 0.0,
255
+ "key::float_one_e_nine": 1.0e9,
256
+ "key::float_minus_one_e_minus_nine": -1.0e-9,
257
+ "key::float_nan": math.nan,
258
+ "key::float_inf": math.inf,
259
+ "key::float_minus_inf": -math.inf,
260
+ "key::float_wrapped": -1000.0,
261
+ },
262
+ "key::str": {
263
+ "key::str_empty": "",
264
+ "key::str": "dummy",
265
+ },
266
+ "key::list":
267
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"],
268
+ "key::tuple":
269
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"],
270
+ },
271
+ ),
272
+ )
273
+ @ddt.unpack
274
+ def test_json_reformat(self, data, expect):
275
+ self.assertTrue(json_compare(json_reformat(data), expect))
276
+
277
+ @ddt.data(
278
+ (set(),),
279
+ (object(),),
280
+ ([set(), object()],),
281
+ ({"set": set(), "object": object()},),
282
+ )
283
+ def test_json_reformat__unregistered_type(self, data):
284
+ with pytest.raises(ValueError):
285
+ json_reformat(data)
286
+
287
+ @ddt.data(
288
+ (None, None),
289
+ (True, True),
290
+ (False, False),
291
+ (1, 1),
292
+ (-1, -1),
293
+ (0, 0),
294
+ (1.0, 1.0),
295
+ (-1.0, -1.0),
296
+ (0.0, 0.0),
297
+ (1.0e9, 1.0e9),
298
+ (-1.0e-9, -1.0e-9),
299
+ (math.nan, "nan"),
300
+ (math.inf, "inf"),
301
+ (-math.inf, "-inf"),
302
+ (MultipliedInt(-1, 100), -100),
303
+ (MultipliedInt(1, 100), 100),
304
+ (MultipliedFloat(1.0, 100.0), 100.0),
305
+ (MultipliedFloat(-1.0, 100.0), -100.0),
306
+ ("", ""),
307
+ ("dummy", "dummy"),
308
+ (
309
+ datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
310
+ "2000-01-01 00:00:00+00:00",
311
+ ),
312
+ (
313
+ [None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, math.nan, math.inf, -math.inf, "", "dummy"],
314
+ [None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, "nan", "inf", "-inf", "", "dummy"],
315
+ ),
316
+ (
317
+ (None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, math.nan, math.inf, -math.inf, "", "dummy"),
318
+ [None, True, False, 1, -1, 0, 1.0, -1.0, 0.0, 1.0e9, -1.0e-9, "nan", "inf", "-inf", "", "dummy"],
319
+ ),
320
+ ({1, 2, 3, 4, 5, 6, 7}, [1, 2, 3, 4, 5, 6, 7]),
321
+ (
322
+ {
323
+ "none": None,
324
+ "bool_true": True,
325
+ "bool_false": False,
326
+ "int_one": 1,
327
+ "int_minus_one": -1,
328
+ "int_zero": 0,
329
+ "int_wrapped": MultipliedInt(1, -1000),
330
+ "float_one": 1.0,
331
+ "float_minus_one": -1.0,
332
+ "float_zero": 0.0,
333
+ "float_one_e_nine": 1.0e9,
334
+ "float_minus_one_e_minus_nine": -1.0e-9,
335
+ "float_nan": math.nan,
336
+ "float_inf": math.inf,
337
+ "float_minus_inf": -math.inf,
338
+ "float_wrapped": MultipliedFloat(1.0, -1000.0),
339
+ "str_empty": "",
340
+ "str": "dummy",
341
+ "datetime": datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
342
+ },
343
+ {
344
+ "none": None,
345
+ "bool_true": True,
346
+ "bool_false": False,
347
+ "int_one": 1,
348
+ "int_minus_one": -1,
349
+ "int_zero": 0,
350
+ "int_wrapped": -1000,
351
+ "float_one": 1.0,
352
+ "float_minus_one": -1.0,
353
+ "float_zero": 0.0,
354
+ "float_one_e_nine": 1.0e9,
355
+ "float_minus_one_e_minus_nine": -1.0e-9,
356
+ "float_nan": "nan",
357
+ "float_inf": "inf",
358
+ "float_minus_inf": "-inf",
359
+ "float_wrapped": -1000.0,
360
+ "str_empty": "",
361
+ "str": "dummy",
362
+ "datetime": "2000-01-01 00:00:00+00:00",
363
+ },
364
+ ),
365
+ (
366
+ {
367
+ "none": None,
368
+ "bool": {
369
+ "bool_true": True,
370
+ "bool_false": False,
371
+ },
372
+ "int": {
373
+ "int_one": 1,
374
+ "int_minus_one": -1,
375
+ "int_zero": 0,
376
+ "int_wrapped": MultipliedInt(1, -1000),
377
+ },
378
+ "float": {
379
+ "float_one": 1.0,
380
+ "float_minus_one": -1.0,
381
+ "float_zero": 0.0,
382
+ "float_one_e_nine": 1.0e9,
383
+ "float_minus_one_e_minus_nine": -1.0e-9,
384
+ "float_nan": math.nan,
385
+ "float_inf": math.inf,
386
+ "float_minus_inf": -math.inf,
387
+ "float_wrapped": MultipliedFloat(1.0, -1000.0),
388
+ },
389
+ "str": {
390
+ "str_empty": "",
391
+ "str": "dummy",
392
+ },
393
+ "datetime": datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
394
+ "list":
395
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"],
396
+ "tuple":
397
+ (None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"),
398
+ "set": {1, 2, 3, 4, 5, 6, 7},
399
+ },
400
+ {
401
+ "none": None,
402
+ "bool": {
403
+ "bool_true": True,
404
+ "bool_false": False,
405
+ },
406
+ "int": {
407
+ "int_one": 1,
408
+ "int_minus_one": -1,
409
+ "int_zero": 0,
410
+ "int_wrapped": -1000,
411
+ },
412
+ "float": {
413
+ "float_one": 1.0,
414
+ "float_minus_one": -1.0,
415
+ "float_zero": 0.0,
416
+ "float_one_e_nine": 1.0e9,
417
+ "float_minus_one_e_minus_nine": -1.0e-9,
418
+ "float_nan": "nan",
419
+ "float_inf": "inf",
420
+ "float_minus_inf": "-inf",
421
+ "float_wrapped": -1000.0,
422
+ },
423
+ "str": {
424
+ "str_empty": "",
425
+ "str": "dummy",
426
+ },
427
+ "datetime": "2000-01-01 00:00:00+00:00",
428
+ "list": [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, "nan", "inf", "-inf", "", "dummy"],
429
+ "tuple": [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, "nan", "inf", "-inf", "", "dummy"],
430
+ "set": [1, 2, 3, 4, 5, 6, 7],
431
+ },
432
+ ),
433
+ (
434
+ WrappedDict({
435
+ PrefixedStr("key", "none"): None,
436
+ PrefixedStr("key", "bool"): WrappedDict({
437
+ PrefixedStr("key", "bool_true"): True,
438
+ PrefixedStr("key", "bool_false"): False,
439
+ }),
440
+ PrefixedStr("key", "int"): WrappedDict({
441
+ PrefixedStr("key", "int_one"): 1,
442
+ PrefixedStr("key", "int_minus_one"): -1,
443
+ PrefixedStr("key", "int_zero"): 0,
444
+ PrefixedStr("key", "int_wrapped"): MultipliedInt(1, -1000),
445
+ }),
446
+ PrefixedStr("key", "float"): WrappedDict({
447
+ PrefixedStr("key", "float_one"): 1.0,
448
+ PrefixedStr("key", "float_minus_one"): -1.0,
449
+ PrefixedStr("key", "float_zero"): 0.0,
450
+ PrefixedStr("key", "float_one_e_nine"): 1.0e9,
451
+ PrefixedStr("key", "float_minus_one_e_minus_nine"): -1.0e-9,
452
+ PrefixedStr("key", "float_nan"): math.nan,
453
+ PrefixedStr("key", "float_inf"): math.inf,
454
+ PrefixedStr("key", "float_minus_inf"): -math.inf,
455
+ PrefixedStr("key", "float_wrapped"): MultipliedFloat(1.0, -1000.0),
456
+ }),
457
+ PrefixedStr("key", "str"): WrappedDict({
458
+ PrefixedStr("key", "str_empty"): "",
459
+ PrefixedStr("key", "str"): "dummy",
460
+ }),
461
+ PrefixedStr("key", "datetime"): datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
462
+ PrefixedStr("key", "list"): WrappedList(
463
+ [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"]),
464
+ PrefixedStr("key", "tuple"):
465
+ (None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, math.nan, math.inf, -math.inf, "", "dummy"),
466
+ PrefixedStr("key", "set"): {1, 2, 3, 4, 5, 6, 7},
467
+ }),
468
+ {
469
+ "key::none": None,
470
+ "key::bool": {
471
+ "key::bool_true": True,
472
+ "key::bool_false": False,
473
+ },
474
+ "key::int": {
475
+ "key::int_one": 1,
476
+ "key::int_minus_one": -1,
477
+ "key::int_zero": 0,
478
+ "key::int_wrapped": -1000,
479
+ },
480
+ "key::float": {
481
+ "key::float_one": 1.0,
482
+ "key::float_minus_one": -1.0,
483
+ "key::float_zero": 0.0,
484
+ "key::float_one_e_nine": 1.0e9,
485
+ "key::float_minus_one_e_minus_nine": -1.0e-9,
486
+ "key::float_nan": "nan",
487
+ "key::float_inf": "inf",
488
+ "key::float_minus_inf": "-inf",
489
+ "key::float_wrapped": -1000.0,
490
+ },
491
+ "key::str": {
492
+ "key::str_empty": "",
493
+ "key::str": "dummy",
494
+ },
495
+ "key::datetime": "2000-01-01 00:00:00+00:00",
496
+ "key::list": [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, "nan", "inf", "-inf", "", "dummy"],
497
+ "key::tuple": [None, True, False, 1, -1, 0, 1., -1., 0., 1e9, -1e-9, "nan", "inf", "-inf", "", "dummy"],
498
+ "key::set": [1, 2, 3, 4, 5, 6, 7],
499
+ },
500
+ ),
501
+ )
502
+ @ddt.unpack
503
+ def test_json_sanitize(self, data, expect):
504
+ self.assertTrue(json_compare(json_sanitize(data), expect))