boto3-assist 0.6.0__py3-none-any.whl → 0.6.1__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.
- boto3_assist/models/serializable_model.py +2 -2
- boto3_assist/utilities/serialization_utility.py +131 -11
- boto3_assist/version.py +1 -1
- {boto3_assist-0.6.0.dist-info → boto3_assist-0.6.1.dist-info}/METADATA +1 -1
- {boto3_assist-0.6.0.dist-info → boto3_assist-0.6.1.dist-info}/RECORD +8 -8
- {boto3_assist-0.6.0.dist-info → boto3_assist-0.6.1.dist-info}/WHEEL +0 -0
- {boto3_assist-0.6.0.dist-info → boto3_assist-0.6.1.dist-info}/licenses/LICENSE-EXPLAINED.txt +0 -0
- {boto3_assist-0.6.0.dist-info → boto3_assist-0.6.1.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -5,5 +5,5 @@ MIT License. See Project Root for the license information.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from boto3_assist.utilities.serialization_utility import SerializableModel
|
|
8
|
+
|
|
9
|
+
from boto3_assist.utilities.serialization_utility import SerializableModel # noqa: F401 # pylint: disable=unused-import
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
"""Serialization Utility"""
|
|
2
2
|
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from decimal import Decimal
|
|
5
|
-
from typing import Dict, List, TypeVar, Any
|
|
6
|
-
import json
|
|
7
|
-
import jsons
|
|
8
3
|
import datetime as dt
|
|
9
4
|
import decimal
|
|
10
5
|
import inspect
|
|
6
|
+
import json
|
|
11
7
|
import uuid
|
|
12
|
-
from
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
from typing import Any, Dict, List, TypeVar
|
|
13
11
|
|
|
12
|
+
from aws_lambda_powertools import Logger
|
|
14
13
|
|
|
15
14
|
T = TypeVar("T")
|
|
16
15
|
|
|
@@ -53,6 +52,15 @@ class SerializableModel:
|
|
|
53
52
|
instance=self, serialize_fn=lambda x: x, include_none=True
|
|
54
53
|
)
|
|
55
54
|
|
|
55
|
+
def to_wide_dictionary(self) -> Dict:
|
|
56
|
+
"""
|
|
57
|
+
Dumps an object to dictionary structure
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
dump = Serialization.to_wide_dictionary(model=self)
|
|
61
|
+
|
|
62
|
+
return dump
|
|
63
|
+
|
|
56
64
|
|
|
57
65
|
class JsonEncoder(json.JSONEncoder):
|
|
58
66
|
"""
|
|
@@ -108,6 +116,32 @@ class Serialization:
|
|
|
108
116
|
|
|
109
117
|
return dump
|
|
110
118
|
|
|
119
|
+
@staticmethod
|
|
120
|
+
def to_wide_dictionary(model: object) -> Dict:
|
|
121
|
+
"""
|
|
122
|
+
Dumps an object to dictionary structure
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
dump = Serialization.to_dict(
|
|
126
|
+
instance=model, serialize_fn=lambda x: x, include_none=True
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# have a dictionary now let's flatten out
|
|
130
|
+
flat_dict = {}
|
|
131
|
+
for key, value in dump.items():
|
|
132
|
+
if isinstance(value, dict):
|
|
133
|
+
for sub_key, sub_value in value.items():
|
|
134
|
+
flat_dict[f"{key}_{sub_key}"] = sub_value
|
|
135
|
+
elif isinstance(value, list):
|
|
136
|
+
for i, sub_value in enumerate(value):
|
|
137
|
+
sub_dict = Serialization.to_wide_dictionary(sub_value)
|
|
138
|
+
for sub_key, sub_value in sub_dict.items():
|
|
139
|
+
flat_dict[f"{key}_{i}_{sub_key}"] = sub_value
|
|
140
|
+
else:
|
|
141
|
+
flat_dict[key] = value
|
|
142
|
+
|
|
143
|
+
return flat_dict
|
|
144
|
+
|
|
111
145
|
@staticmethod
|
|
112
146
|
def map(source: object, target: T, coerce: bool = True) -> T | None:
|
|
113
147
|
"""Map an object from one object to another"""
|
|
@@ -118,12 +152,92 @@ class Serialization:
|
|
|
118
152
|
source_dict = Serialization.convert_object_to_dict(source)
|
|
119
153
|
if not isinstance(source_dict, dict):
|
|
120
154
|
return None
|
|
121
|
-
return Serialization.
|
|
155
|
+
return Serialization._load_properties(
|
|
122
156
|
source=source_dict, target=target, coerce=coerce
|
|
123
157
|
)
|
|
124
158
|
|
|
125
159
|
@staticmethod
|
|
126
|
-
def
|
|
160
|
+
def to_wide_dictionary_list(
|
|
161
|
+
data: Dict[str, Any] | List[Dict[str, Any]],
|
|
162
|
+
remove_collisions: bool = True,
|
|
163
|
+
raise_error_on_collision: bool = False,
|
|
164
|
+
) -> List[Dict[str, Any]]:
|
|
165
|
+
"""
|
|
166
|
+
Converts a dictionary or list of dictionaries to a list of dictionaries.
|
|
167
|
+
|
|
168
|
+
:param data: Dictionary or list of dictionaries to be converted
|
|
169
|
+
:param remove_collisions: If True, removes duplicate keys from the dictionaries
|
|
170
|
+
:return: List of dictionaries
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
collisions = []
|
|
174
|
+
|
|
175
|
+
def recursive_flatten(prefix, obj):
|
|
176
|
+
"""
|
|
177
|
+
Recursively flattens a JSON object.
|
|
178
|
+
|
|
179
|
+
:param prefix: Current key prefix
|
|
180
|
+
:param obj: Object to flatten
|
|
181
|
+
:return: List of flattened dictionaries
|
|
182
|
+
"""
|
|
183
|
+
if isinstance(obj, list):
|
|
184
|
+
result = []
|
|
185
|
+
for _, item in enumerate(obj):
|
|
186
|
+
x = recursive_flatten("", item)
|
|
187
|
+
result.extend(x)
|
|
188
|
+
return result
|
|
189
|
+
elif isinstance(obj, dict):
|
|
190
|
+
result = [{}]
|
|
191
|
+
for key, value in obj.items():
|
|
192
|
+
sub_result = recursive_flatten(
|
|
193
|
+
f"{prefix}_{key}" if prefix else key, value
|
|
194
|
+
)
|
|
195
|
+
new_result = []
|
|
196
|
+
for entry in result:
|
|
197
|
+
for sub_entry in sub_result:
|
|
198
|
+
# remove any collisions
|
|
199
|
+
|
|
200
|
+
for k in entry:
|
|
201
|
+
if k in sub_entry:
|
|
202
|
+
if k not in collisions:
|
|
203
|
+
logger.debug(f"Collision detected: {k}")
|
|
204
|
+
collisions.append(k)
|
|
205
|
+
merged = entry.copy()
|
|
206
|
+
merged.update(sub_entry)
|
|
207
|
+
new_result.append(merged)
|
|
208
|
+
result = new_result
|
|
209
|
+
return result
|
|
210
|
+
else:
|
|
211
|
+
return [{prefix: obj}] if prefix else []
|
|
212
|
+
|
|
213
|
+
results = recursive_flatten("", data)
|
|
214
|
+
if remove_collisions:
|
|
215
|
+
results = Serialization.remove_collisions(results, collisions)
|
|
216
|
+
|
|
217
|
+
if raise_error_on_collision and len(collisions) > 0:
|
|
218
|
+
raise ValueError(f"Duplicate keys detected: {collisions}")
|
|
219
|
+
|
|
220
|
+
return results
|
|
221
|
+
|
|
222
|
+
@staticmethod
|
|
223
|
+
def remove_collisions(
|
|
224
|
+
data: List[Dict[str, Any]], collisions: List[str]
|
|
225
|
+
) -> List[Dict[str, Any]]:
|
|
226
|
+
"""
|
|
227
|
+
Removes collisions from a list of dictionaries.
|
|
228
|
+
|
|
229
|
+
:param data: List of dictionaries
|
|
230
|
+
:param collisions: List of collision keys
|
|
231
|
+
:return: List of dictionaries with collisions removed
|
|
232
|
+
"""
|
|
233
|
+
for c in collisions:
|
|
234
|
+
for r in data:
|
|
235
|
+
if c in r:
|
|
236
|
+
del r[c]
|
|
237
|
+
return data
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def _load_properties(
|
|
127
241
|
source: dict,
|
|
128
242
|
target: T,
|
|
129
243
|
coerce: bool = True,
|
|
@@ -183,9 +297,9 @@ class Serialization:
|
|
|
183
297
|
attr.clear()
|
|
184
298
|
attr.extend(value)
|
|
185
299
|
elif isinstance(attr, dict) and isinstance(value, dict):
|
|
186
|
-
Serialization.
|
|
300
|
+
Serialization._load_properties(value, attr, coerce=coerce)
|
|
187
301
|
elif hasattr(attr, "__dict__") and isinstance(value, dict):
|
|
188
|
-
Serialization.
|
|
302
|
+
Serialization._load_properties(value, attr, coerce=coerce)
|
|
189
303
|
else:
|
|
190
304
|
setattr(target, key, value)
|
|
191
305
|
except ValueError as e:
|
|
@@ -230,12 +344,18 @@ class Serialization:
|
|
|
230
344
|
|
|
231
345
|
@staticmethod
|
|
232
346
|
def to_dict(
|
|
233
|
-
instance: SerializableModel,
|
|
347
|
+
instance: SerializableModel | dict,
|
|
234
348
|
serialize_fn,
|
|
235
349
|
include_none: bool = True,
|
|
236
350
|
) -> Dict[str, Any]:
|
|
237
351
|
"""To Dict / Dictionary"""
|
|
238
352
|
|
|
353
|
+
if instance is None:
|
|
354
|
+
return {}
|
|
355
|
+
|
|
356
|
+
if isinstance(instance, dict):
|
|
357
|
+
return instance
|
|
358
|
+
|
|
239
359
|
def is_primitive(value):
|
|
240
360
|
"""Check if the value is a primitive data type."""
|
|
241
361
|
return isinstance(value, (str, int, bool, type(None)))
|
boto3_assist/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.6.
|
|
1
|
+
__version__ = '0.6.1'
|
|
@@ -3,7 +3,7 @@ boto3_assist/boto3session.py,sha256=Q9sByNC0r_aMQfHnIEnxtTaiCMUqikm8UeSTxV7-Np0,
|
|
|
3
3
|
boto3_assist/connection.py,sha256=CNGkAIRyfrELoWrV0ziQBA3oHacNFuLL3i8faUPRiO0,3486
|
|
4
4
|
boto3_assist/connection_tracker.py,sha256=bfImvNVX-0Lhb-ombOurWUpNLdI0qVDil-kokBdIFkY,4345
|
|
5
5
|
boto3_assist/http_status_codes.py,sha256=G0zRSWenwavYKETvDF9tNVUXQz3Ae2gXdBETYbjvJe8,3284
|
|
6
|
-
boto3_assist/version.py,sha256=
|
|
6
|
+
boto3_assist/version.py,sha256=gd3s3RotD0_KL90Tua-YkOr60Jm2C2_wvlEhAT08068,22
|
|
7
7
|
boto3_assist/aws_lambda/event_info.py,sha256=OkZ4WzuGaHEu_T8sB188KBgShAJhZpWASALKRGBOhMg,14648
|
|
8
8
|
boto3_assist/cloudwatch/cloudwatch_connection.py,sha256=mnGWaLSQpHh5EeY7Ek_2o9JKHJxOELIYtQVMX1IaHn4,2480
|
|
9
9
|
boto3_assist/cloudwatch/cloudwatch_connection_tracker.py,sha256=mzRtO1uHrcfJNh1XrGEiXdTqxwEP8d1RqJkraMNkgK0,410
|
|
@@ -34,7 +34,7 @@ boto3_assist/environment_services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
|
|
|
34
34
|
boto3_assist/environment_services/environment_loader.py,sha256=zCA4mRdVWMLKzjDRvrJbhQfRVP4HAMGpuQFi07zzULk,3396
|
|
35
35
|
boto3_assist/environment_services/environment_variables.py,sha256=4ccBKdPt6O7hcRT3zBHd8vqu8yQU8udmoD5RLAT3iMs,6801
|
|
36
36
|
boto3_assist/errors/custom_exceptions.py,sha256=zC2V2Y4PUtKj3uLPn8mB-JessksKWJWvKM9kp1dmvt8,760
|
|
37
|
-
boto3_assist/models/serializable_model.py,sha256=
|
|
37
|
+
boto3_assist/models/serializable_model.py,sha256=ZMrRJRvJWLY8PBSKK_nPCgYKv1qUxDPEVdcADKbIHsI,266
|
|
38
38
|
boto3_assist/s3/s3.py,sha256=DFCJs5z1mMIT8nZfnqPyr_cvhi9-FePuYH--tzD7b5E,17104
|
|
39
39
|
boto3_assist/s3/s3_connection.py,sha256=FI1AhZV4UbTXQRTb4TqL9mv88Gt018rPZVFBvLetVAw,2163
|
|
40
40
|
boto3_assist/utilities/datetime_utility.py,sha256=dgAMB9VqakrYIPXlSoVQiLSsc_yhrJK4gMfJO9mX90w,11112
|
|
@@ -43,10 +43,10 @@ boto3_assist/utilities/file_operations.py,sha256=Zy8fu8fpuVNf7U9NimrLdy5FRF71XSI
|
|
|
43
43
|
boto3_assist/utilities/http_utility.py,sha256=koFv7Va-8ng-47Nt1K2Sh7Ti95e62IYs9VMLlGh9Kt4,1173
|
|
44
44
|
boto3_assist/utilities/logging_utility.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
45
|
boto3_assist/utilities/numbers_utility.py,sha256=KIiNkBSRbfNWvtXG5SdHp625LTiW12VtADUa4ZlWMFo,8709
|
|
46
|
-
boto3_assist/utilities/serialization_utility.py,sha256=
|
|
46
|
+
boto3_assist/utilities/serialization_utility.py,sha256=KJhit0lI1lr8hhndW6UhKQSZD3c2RH555Ni7eP-e5Ms,16557
|
|
47
47
|
boto3_assist/utilities/string_utility.py,sha256=sBY80aQO-fTRanlHryZFMQBxdo6OvLRvnZjZrQepHlI,9283
|
|
48
|
-
boto3_assist-0.6.
|
|
49
|
-
boto3_assist-0.6.
|
|
50
|
-
boto3_assist-0.6.
|
|
51
|
-
boto3_assist-0.6.
|
|
52
|
-
boto3_assist-0.6.
|
|
48
|
+
boto3_assist-0.6.1.dist-info/METADATA,sha256=MVB3qF040hI48bRH8xhXXkyX9aewgYUUiGtUubi2l-0,1728
|
|
49
|
+
boto3_assist-0.6.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
50
|
+
boto3_assist-0.6.1.dist-info/licenses/LICENSE-EXPLAINED.txt,sha256=WFREvTpfTjPjDHpOLADxJpCKpIla3Ht87RUUGii4ODU,606
|
|
51
|
+
boto3_assist-0.6.1.dist-info/licenses/LICENSE.txt,sha256=PXDhFWS5L5aOTkVhNvoitHKbAkgxqMI2uUPQyrnXGiI,1105
|
|
52
|
+
boto3_assist-0.6.1.dist-info/RECORD,,
|
|
File without changes
|
{boto3_assist-0.6.0.dist-info → boto3_assist-0.6.1.dist-info}/licenses/LICENSE-EXPLAINED.txt
RENAMED
|
File without changes
|
|
File without changes
|