everysk-lib 1.10.2__cp312-cp312-win_amd64.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.
- everysk/__init__.py +30 -0
- everysk/_version.py +683 -0
- everysk/api/__init__.py +61 -0
- everysk/api/api_requestor.py +167 -0
- everysk/api/api_resources/__init__.py +23 -0
- everysk/api/api_resources/api_resource.py +371 -0
- everysk/api/api_resources/calculation.py +779 -0
- everysk/api/api_resources/custom_index.py +42 -0
- everysk/api/api_resources/datastore.py +81 -0
- everysk/api/api_resources/file.py +42 -0
- everysk/api/api_resources/market_data.py +223 -0
- everysk/api/api_resources/parser.py +66 -0
- everysk/api/api_resources/portfolio.py +43 -0
- everysk/api/api_resources/private_security.py +42 -0
- everysk/api/api_resources/report.py +65 -0
- everysk/api/api_resources/report_template.py +39 -0
- everysk/api/api_resources/tests.py +115 -0
- everysk/api/api_resources/worker_execution.py +64 -0
- everysk/api/api_resources/workflow.py +65 -0
- everysk/api/api_resources/workflow_execution.py +93 -0
- everysk/api/api_resources/workspace.py +42 -0
- everysk/api/http_client.py +63 -0
- everysk/api/tests.py +32 -0
- everysk/api/utils.py +262 -0
- everysk/config.py +451 -0
- everysk/core/_tests/serialize/test_json.py +336 -0
- everysk/core/_tests/serialize/test_orjson.py +295 -0
- everysk/core/_tests/serialize/test_pickle.py +48 -0
- everysk/core/cloud_function/main.py +78 -0
- everysk/core/cloud_function/tests.py +86 -0
- everysk/core/compress.py +245 -0
- everysk/core/datetime/__init__.py +12 -0
- everysk/core/datetime/calendar.py +144 -0
- everysk/core/datetime/date.py +424 -0
- everysk/core/datetime/date_expression.py +299 -0
- everysk/core/datetime/date_mixin.py +1475 -0
- everysk/core/datetime/date_settings.py +30 -0
- everysk/core/datetime/datetime.py +713 -0
- everysk/core/exceptions.py +435 -0
- everysk/core/fields.py +1176 -0
- everysk/core/firestore.py +555 -0
- everysk/core/fixtures/_settings.py +29 -0
- everysk/core/fixtures/other/_settings.py +18 -0
- everysk/core/fixtures/user_agents.json +88 -0
- everysk/core/http.py +691 -0
- everysk/core/lists.py +92 -0
- everysk/core/log.py +709 -0
- everysk/core/number.py +37 -0
- everysk/core/object.py +1469 -0
- everysk/core/redis.py +1021 -0
- everysk/core/retry.py +51 -0
- everysk/core/serialize.py +674 -0
- everysk/core/sftp.py +414 -0
- everysk/core/signing.py +53 -0
- everysk/core/slack.py +127 -0
- everysk/core/string.py +199 -0
- everysk/core/tests.py +240 -0
- everysk/core/threads.py +199 -0
- everysk/core/undefined.py +70 -0
- everysk/core/unittests.py +73 -0
- everysk/core/workers.py +241 -0
- everysk/sdk/__init__.py +23 -0
- everysk/sdk/base.py +98 -0
- everysk/sdk/brutils/cnpj.py +391 -0
- everysk/sdk/brutils/cnpj_pd.py +129 -0
- everysk/sdk/engines/__init__.py +26 -0
- everysk/sdk/engines/cache.py +185 -0
- everysk/sdk/engines/compliance.py +37 -0
- everysk/sdk/engines/cryptography.py +69 -0
- everysk/sdk/engines/expression.cp312-win_amd64.pyd +0 -0
- everysk/sdk/engines/expression.pyi +55 -0
- everysk/sdk/engines/helpers.cp312-win_amd64.pyd +0 -0
- everysk/sdk/engines/helpers.pyi +26 -0
- everysk/sdk/engines/lock.py +120 -0
- everysk/sdk/engines/market_data.py +244 -0
- everysk/sdk/engines/settings.py +19 -0
- everysk/sdk/entities/__init__.py +23 -0
- everysk/sdk/entities/base.py +784 -0
- everysk/sdk/entities/base_list.py +131 -0
- everysk/sdk/entities/custom_index/base.py +209 -0
- everysk/sdk/entities/custom_index/settings.py +29 -0
- everysk/sdk/entities/datastore/base.py +160 -0
- everysk/sdk/entities/datastore/settings.py +17 -0
- everysk/sdk/entities/fields.py +375 -0
- everysk/sdk/entities/file/base.py +215 -0
- everysk/sdk/entities/file/settings.py +63 -0
- everysk/sdk/entities/portfolio/base.py +248 -0
- everysk/sdk/entities/portfolio/securities.py +241 -0
- everysk/sdk/entities/portfolio/security.py +580 -0
- everysk/sdk/entities/portfolio/settings.py +97 -0
- everysk/sdk/entities/private_security/base.py +226 -0
- everysk/sdk/entities/private_security/settings.py +17 -0
- everysk/sdk/entities/query.py +603 -0
- everysk/sdk/entities/report/base.py +214 -0
- everysk/sdk/entities/report/settings.py +23 -0
- everysk/sdk/entities/script.py +310 -0
- everysk/sdk/entities/secrets/base.py +128 -0
- everysk/sdk/entities/secrets/script.py +119 -0
- everysk/sdk/entities/secrets/settings.py +17 -0
- everysk/sdk/entities/settings.py +48 -0
- everysk/sdk/entities/tags.py +174 -0
- everysk/sdk/entities/worker_execution/base.py +307 -0
- everysk/sdk/entities/worker_execution/settings.py +63 -0
- everysk/sdk/entities/workflow_execution/base.py +113 -0
- everysk/sdk/entities/workflow_execution/settings.py +32 -0
- everysk/sdk/entities/workspace/base.py +99 -0
- everysk/sdk/entities/workspace/settings.py +27 -0
- everysk/sdk/settings.py +67 -0
- everysk/sdk/tests.py +105 -0
- everysk/sdk/worker_base.py +47 -0
- everysk/server/__init__.py +9 -0
- everysk/server/applications.py +63 -0
- everysk/server/endpoints.py +516 -0
- everysk/server/example_api.py +69 -0
- everysk/server/middlewares.py +80 -0
- everysk/server/requests.py +62 -0
- everysk/server/responses.py +119 -0
- everysk/server/routing.py +64 -0
- everysk/server/settings.py +36 -0
- everysk/server/tests.py +36 -0
- everysk/settings.py +98 -0
- everysk/sql/__init__.py +9 -0
- everysk/sql/connection.py +232 -0
- everysk/sql/model.py +376 -0
- everysk/sql/query.py +417 -0
- everysk/sql/row_factory.py +63 -0
- everysk/sql/settings.py +49 -0
- everysk/sql/utils.py +129 -0
- everysk/tests.py +23 -0
- everysk/utils.py +81 -0
- everysk/version.py +15 -0
- everysk_lib-1.10.2.dist-info/.gitignore +5 -0
- everysk_lib-1.10.2.dist-info/METADATA +326 -0
- everysk_lib-1.10.2.dist-info/RECORD +137 -0
- everysk_lib-1.10.2.dist-info/WHEEL +5 -0
- everysk_lib-1.10.2.dist-info/licenses/LICENSE.txt +9 -0
- everysk_lib-1.10.2.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# (C) Copyright 2025 EVERYSK TECHNOLOGIES
|
|
4
|
+
#
|
|
5
|
+
# This is an unpublished work containing confidential and proprietary
|
|
6
|
+
# information of EVERYSK TECHNOLOGIES. Disclosure, use, or reproduction
|
|
7
|
+
# without authorization of EVERYSK TECHNOLOGIES is prohibited.
|
|
8
|
+
#
|
|
9
|
+
###############################################################################
|
|
10
|
+
__all__ = ['dumps', 'loads']
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import pickle
|
|
14
|
+
from base64 import b64decode, b64encode
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from everysk.config import settings
|
|
18
|
+
from everysk.core.datetime import Date, DateTime
|
|
19
|
+
from everysk.core.object import CLASS_KEY, BaseObject
|
|
20
|
+
from everysk.core.signing import sign, unsign
|
|
21
|
+
from everysk.core.string import import_from_string
|
|
22
|
+
from everysk.core.undefined import UndefinedType
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
from simplejson import JSONEncoder as _JSONEncoder # type: ignore
|
|
26
|
+
except ImportError:
|
|
27
|
+
from json import JSONEncoder as _JSONEncoder
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
BYTES_KEY: str = '__bytes__'
|
|
31
|
+
DATE_KEY: str = '__date__'
|
|
32
|
+
DATETIME_KEY: str = '__datetime__'
|
|
33
|
+
PICKLE_KEY_SEPARATOR: str = ':'
|
|
34
|
+
UNDEFINED_KEY: str = '__undefined__'
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Helper functions
|
|
38
|
+
def _parser(obj: bool | int | float | str | list | dict, object_hook: callable) -> Any:
|
|
39
|
+
"""
|
|
40
|
+
Recursively parse the object to handle custom object hooks for date, datetime, Undefined, BaseObject and BaseDict.
|
|
41
|
+
The obj parameter will be always the result from the orjson.loads function, so it will be a boolean, int, float, str, list or dict.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
obj (bool | int | float | str | list | dict): The result from the orjson.loads function.
|
|
45
|
+
object_hook (callable): The object hook function to use to convert the object.
|
|
46
|
+
"""
|
|
47
|
+
if isinstance(obj, list):
|
|
48
|
+
# Using the same list consumes less memory
|
|
49
|
+
for index, item in enumerate(obj):
|
|
50
|
+
# We only need to parse lists and dicts
|
|
51
|
+
if isinstance(item, (list, dict)):
|
|
52
|
+
obj[index] = _parser(item, object_hook)
|
|
53
|
+
|
|
54
|
+
elif isinstance(obj, dict):
|
|
55
|
+
# Using the same dict consumes less memory
|
|
56
|
+
for key, value in obj.items():
|
|
57
|
+
# We only need to parse lists and dicts
|
|
58
|
+
if isinstance(value, (list, dict)):
|
|
59
|
+
obj[key] = _parser(value, object_hook)
|
|
60
|
+
|
|
61
|
+
# The object_hook is called only for dict objects
|
|
62
|
+
obj = object_hook(obj)
|
|
63
|
+
|
|
64
|
+
return obj
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def convert_bytes_decode(obj: bytes, encoding: str = 'utf-8') -> str:
|
|
68
|
+
"""
|
|
69
|
+
Convert a bytes object to a string if possible, otherwise encode it in base64.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
obj (bytes): The bytes object to convert to a string.
|
|
73
|
+
encoding (str): The encoding to use to decode the bytes object. Default is 'utf-8'.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
str: The string representation of the bytes object.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
return obj.decode(encoding)
|
|
80
|
+
except UnicodeDecodeError:
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
return b64encode(obj).decode(encoding)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def convert_bytes(obj: bytes) -> dict[str, dict[str, str]]:
|
|
87
|
+
"""
|
|
88
|
+
Convert a bytes object to a dict.
|
|
89
|
+
If the bytes object is not a valid utf-8 string, it will be encoded in base64.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
obj (bytes): The bytes object to convert to a dict.
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
encoding = 'utf-8'
|
|
96
|
+
value = obj.decode(encoding)
|
|
97
|
+
except UnicodeDecodeError:
|
|
98
|
+
value = b64encode(obj).decode('utf-8')
|
|
99
|
+
encoding = 'base64'
|
|
100
|
+
|
|
101
|
+
return {BYTES_KEY: {'encoding': encoding, 'value': value}}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def handle_bytes_conversion(obj: bytes, *, decode_bytes: bool) -> str | dict:
|
|
105
|
+
"""
|
|
106
|
+
Handle the conversion of bytes objects based on the decode_bytes flag.
|
|
107
|
+
If decode_bytes is True, it decodes the bytes object to a string.
|
|
108
|
+
If decode_bytes is False, it converts the bytes object to a dict.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
obj (bytes): The bytes object to convert.
|
|
112
|
+
decode_bytes (bool): Flag to determine whether to decode bytes or convert to dict.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
str | dict: The converted object, either as a string or a dict.
|
|
116
|
+
"""
|
|
117
|
+
if decode_bytes:
|
|
118
|
+
return convert_bytes_decode(obj)
|
|
119
|
+
return convert_bytes(obj)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
###############################################################################
|
|
123
|
+
# JSONEncoder Class Implementation
|
|
124
|
+
###############################################################################
|
|
125
|
+
class JSONEncoder(_JSONEncoder):
|
|
126
|
+
## Public attributes
|
|
127
|
+
add_class_path: bool = True
|
|
128
|
+
date_format: str = None
|
|
129
|
+
datetime_format: str = None
|
|
130
|
+
use_undefined: bool = False
|
|
131
|
+
decode_bytes: bool = False
|
|
132
|
+
|
|
133
|
+
def __init__(self, **kwargs) -> None:
|
|
134
|
+
# Set specific params
|
|
135
|
+
self.add_class_path = kwargs.pop('add_class_path', self.add_class_path)
|
|
136
|
+
self.date_format = kwargs.pop('date_format', self.date_format)
|
|
137
|
+
self.datetime_format = kwargs.pop('datetime_format', self.datetime_format)
|
|
138
|
+
self.use_undefined = kwargs.pop('use_undefined', self.use_undefined)
|
|
139
|
+
self.decode_bytes = kwargs.pop('decode_bytes', self.decode_bytes)
|
|
140
|
+
|
|
141
|
+
# Set all default params
|
|
142
|
+
super().__init__(**kwargs)
|
|
143
|
+
|
|
144
|
+
def undefined_to_dict(self, obj) -> dict[str, None] | None: # pylint: disable=unused-argument
|
|
145
|
+
"""
|
|
146
|
+
Convert an Undefined object to a string or None.
|
|
147
|
+
If `self.use_undefined` is set to True, it returns the default parse string of the Undefined object.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
obj (UndefinedType): The Undefined object to convert to a string.
|
|
151
|
+
"""
|
|
152
|
+
if self.use_undefined:
|
|
153
|
+
return {UNDEFINED_KEY: None}
|
|
154
|
+
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
def convert_date(self, obj: Date) -> dict[str, str] | str:
|
|
158
|
+
"""
|
|
159
|
+
Convert a Date object to a dict or string.
|
|
160
|
+
If `self.date_format` is set the result will be a string in this format
|
|
161
|
+
otherwise the result will be a dict with the Date as isoformat.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
obj (Date): The Date object to convert to a string or dict.
|
|
165
|
+
"""
|
|
166
|
+
if self.date_format:
|
|
167
|
+
return obj.strftime(self.date_format)
|
|
168
|
+
|
|
169
|
+
return {DATE_KEY: obj.isoformat()}
|
|
170
|
+
|
|
171
|
+
def convert_datetime(self, obj: DateTime) -> dict[str, str] | str:
|
|
172
|
+
"""
|
|
173
|
+
Convert a DateTime object to a dict or string.
|
|
174
|
+
If `self.datetime_format` is set the result will be a string in this format
|
|
175
|
+
otherwise the result will be a dict with the DateTime as isoformat.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
obj (DateTime): The DateTime object to convert to a string.
|
|
179
|
+
"""
|
|
180
|
+
if self.datetime_format:
|
|
181
|
+
return obj.strftime(self.datetime_format)
|
|
182
|
+
|
|
183
|
+
return {DATETIME_KEY: obj.isoformat()}
|
|
184
|
+
|
|
185
|
+
def convert_base_object(self, obj: Any) -> Any:
|
|
186
|
+
"""
|
|
187
|
+
At this point we've translated the model instance into Python native datatypes.
|
|
188
|
+
To finalize the serialization process we render the data into json.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
obj (Any): The object to convert to a JSON serializable format.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Any: The JSON serializable
|
|
195
|
+
"""
|
|
196
|
+
func = getattr(obj, settings.SERIALIZE_CONVERT_METHOD_NAME)
|
|
197
|
+
return func(add_class_path=self.add_class_path, recursion=False)
|
|
198
|
+
|
|
199
|
+
def default(self, obj: Any) -> dict | None: # pylint: disable=arguments-renamed
|
|
200
|
+
"""
|
|
201
|
+
Convert an object to a JSON serializable format.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
obj (Any): The object to convert to a JSON serializable format.
|
|
205
|
+
"""
|
|
206
|
+
if Date.is_date(obj):
|
|
207
|
+
return self.convert_date(obj)
|
|
208
|
+
|
|
209
|
+
if DateTime.is_datetime(obj):
|
|
210
|
+
return self.convert_datetime(obj)
|
|
211
|
+
|
|
212
|
+
if hasattr(obj, settings.SERIALIZE_CONVERT_METHOD_NAME):
|
|
213
|
+
return self.convert_base_object(obj)
|
|
214
|
+
|
|
215
|
+
if isinstance(obj, (frozenset, set)):
|
|
216
|
+
return list(obj)
|
|
217
|
+
|
|
218
|
+
if isinstance(obj, bytes):
|
|
219
|
+
return handle_bytes_conversion(obj, decode_bytes=self.decode_bytes)
|
|
220
|
+
|
|
221
|
+
if obj is Undefined:
|
|
222
|
+
return self.undefined_to_dict(obj)
|
|
223
|
+
|
|
224
|
+
return super().default(obj)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
###############################################################################
|
|
228
|
+
# JSONDecoder Class Implementation
|
|
229
|
+
###############################################################################
|
|
230
|
+
class JSONDecoder(json.JSONDecoder):
|
|
231
|
+
## Public attributes
|
|
232
|
+
add_class_path: bool = True
|
|
233
|
+
date_format: str = None
|
|
234
|
+
datetime_format: str = None
|
|
235
|
+
instantiate_object: bool = True
|
|
236
|
+
use_undefined: bool = False
|
|
237
|
+
|
|
238
|
+
def __init__(self, **kwargs) -> None:
|
|
239
|
+
# Set specific params
|
|
240
|
+
self.add_class_path = kwargs.pop('add_class_path', self.add_class_path)
|
|
241
|
+
self.date_format = kwargs.pop('date_format', self.date_format)
|
|
242
|
+
self.datetime_format = kwargs.pop('datetime_format', self.datetime_format)
|
|
243
|
+
self.instantiate_object = kwargs.pop('instantiate_object', self.instantiate_object)
|
|
244
|
+
self.use_undefined = kwargs.pop('use_undefined', self.use_undefined)
|
|
245
|
+
|
|
246
|
+
if 'object_hook' not in kwargs or kwargs['object_hook'] is None:
|
|
247
|
+
kwargs['object_hook'] = self.custom_object_hook
|
|
248
|
+
|
|
249
|
+
# Set all default params
|
|
250
|
+
super().__init__(**kwargs)
|
|
251
|
+
|
|
252
|
+
def dict_to_bytes(self, obj: dict) -> bytes:
|
|
253
|
+
"""
|
|
254
|
+
Convert a bytes dict generated from dumps to a bytes object.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
obj (dict): The dict to convert to a bytes object.
|
|
258
|
+
"""
|
|
259
|
+
encoding = obj['encoding']
|
|
260
|
+
value = obj['value']
|
|
261
|
+
if encoding == 'base64':
|
|
262
|
+
return b64decode(value)
|
|
263
|
+
|
|
264
|
+
return value.encode(encoding)
|
|
265
|
+
|
|
266
|
+
def str_to_date(self, obj: str) -> Date:
|
|
267
|
+
"""
|
|
268
|
+
Convert a string to a Date object.
|
|
269
|
+
If `self.date_format` is set, it uses the specified date format to convert the string to a Date object.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
obj (str): The string to convert to a Date object.
|
|
273
|
+
"""
|
|
274
|
+
if self.date_format:
|
|
275
|
+
return Date.strptime(obj, self.date_format)
|
|
276
|
+
|
|
277
|
+
return Date.fromisoformat(obj)
|
|
278
|
+
|
|
279
|
+
def str_to_datetime(self, obj: str) -> DateTime:
|
|
280
|
+
"""
|
|
281
|
+
Convert a string to a DateTime object.
|
|
282
|
+
If `self.datetime_format` is set, it uses the specified datetime format to convert the string to a DateTime object.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
obj (str): The string to convert to a DateTime object.
|
|
286
|
+
"""
|
|
287
|
+
if self.datetime_format:
|
|
288
|
+
return DateTime.strptime(obj, self.datetime_format)
|
|
289
|
+
|
|
290
|
+
return DateTime.fromisoformat(obj)
|
|
291
|
+
|
|
292
|
+
def str_to_undefined(self, obj: str) -> UndefinedType | None: # pylint: disable=unused-argument
|
|
293
|
+
"""
|
|
294
|
+
Convert a string to Undefined object, if `self.use_undefined` is set to True, otherwise return None.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
obj (str): The string to convert to a Undefined object.
|
|
298
|
+
"""
|
|
299
|
+
if self.use_undefined:
|
|
300
|
+
return Undefined
|
|
301
|
+
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
def import_from_string(self, path: str, obj: dict) -> BaseObject:
|
|
305
|
+
"""
|
|
306
|
+
Import a class from a string path.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
path (str): The path to the class to import.
|
|
310
|
+
"""
|
|
311
|
+
try:
|
|
312
|
+
klass = import_from_string(path)
|
|
313
|
+
except ImportError:
|
|
314
|
+
klass = BaseObject
|
|
315
|
+
|
|
316
|
+
return klass(**obj)
|
|
317
|
+
|
|
318
|
+
def _get_correct_path(self, path: str) -> str:
|
|
319
|
+
"""
|
|
320
|
+
Get the correct path for the class.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
path (str): The path to the class to import.
|
|
324
|
+
"""
|
|
325
|
+
klass_name: str = path.split('.')[-1]
|
|
326
|
+
if klass_name in settings.EVERYSK_SDK_ENTITIES_MODULES_PATH:
|
|
327
|
+
return settings.EVERYSK_SDK_ENTITIES_MODULES_PATH[klass_name]
|
|
328
|
+
if klass_name in settings.EVERYSK_SDK_ENGINES_MODULES_PATH:
|
|
329
|
+
return settings.EVERYSK_SDK_ENGINES_MODULES_PATH[klass_name]
|
|
330
|
+
if klass_name in settings.EVERYSK_SDK_MODULES_PATH:
|
|
331
|
+
return settings.EVERYSK_SDK_MODULES_PATH[klass_name]
|
|
332
|
+
|
|
333
|
+
return path
|
|
334
|
+
|
|
335
|
+
def dict_to_base_obj_class(self, obj: dict) -> BaseObject | dict:
|
|
336
|
+
"""
|
|
337
|
+
Convert a dictionary to a instance of BaseObject or return
|
|
338
|
+
the dictionary if the flag instantiate_object is False.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
obj (dict): A dictionary.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
BaseObject: The BaseObject instance.
|
|
345
|
+
"""
|
|
346
|
+
path: str = obj.pop(CLASS_KEY)
|
|
347
|
+
if not self.instantiate_object:
|
|
348
|
+
return obj
|
|
349
|
+
|
|
350
|
+
# For SDK classes we need to change the path to avoid problems inside the Client project
|
|
351
|
+
if path.startswith('everysk.sdk.') or path.startswith('engines.'):
|
|
352
|
+
path = self._get_correct_path(path)
|
|
353
|
+
|
|
354
|
+
return self.import_from_string(path, obj)
|
|
355
|
+
|
|
356
|
+
def custom_object_hook(self, obj: dict) -> UndefinedType | None | Date | DateTime:
|
|
357
|
+
"""
|
|
358
|
+
We change the default object hook to handle custom object hooks for date, datetime and Undefined objects.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
obj (dict): A dictionary object to convert to check and convert.
|
|
362
|
+
"""
|
|
363
|
+
if BYTES_KEY in obj:
|
|
364
|
+
return self.dict_to_bytes(obj[BYTES_KEY])
|
|
365
|
+
|
|
366
|
+
if DATE_KEY in obj:
|
|
367
|
+
return self.str_to_date(obj[DATE_KEY])
|
|
368
|
+
|
|
369
|
+
if DATETIME_KEY in obj:
|
|
370
|
+
return self.str_to_datetime(obj[DATETIME_KEY])
|
|
371
|
+
|
|
372
|
+
if CLASS_KEY in obj:
|
|
373
|
+
return self.dict_to_base_obj_class(obj)
|
|
374
|
+
|
|
375
|
+
if UNDEFINED_KEY in obj:
|
|
376
|
+
return self.str_to_undefined(obj[UNDEFINED_KEY])
|
|
377
|
+
|
|
378
|
+
return obj
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
###############################################################################
|
|
382
|
+
# Public Functions Implementation
|
|
383
|
+
###############################################################################
|
|
384
|
+
def dumps(
|
|
385
|
+
obj: Any,
|
|
386
|
+
*, # Limits that only named arguments can be passed after this
|
|
387
|
+
allow_nan: bool = True,
|
|
388
|
+
check_circular: bool = True,
|
|
389
|
+
cls: JSONEncoder = JSONEncoder,
|
|
390
|
+
date_format: str = None,
|
|
391
|
+
datetime_format: str = None,
|
|
392
|
+
default: callable = None,
|
|
393
|
+
ensure_ascii: bool = True,
|
|
394
|
+
indent: int = None,
|
|
395
|
+
protocol: str = 'json',
|
|
396
|
+
separators: tuple = None,
|
|
397
|
+
skipkeys: bool = False,
|
|
398
|
+
sort_keys: bool = False,
|
|
399
|
+
use_undefined: bool = False,
|
|
400
|
+
add_class_path: bool = True,
|
|
401
|
+
return_type: str = 'str',
|
|
402
|
+
decode_bytes: bool = False,
|
|
403
|
+
**kwargs,
|
|
404
|
+
) -> str | bytes:
|
|
405
|
+
"""
|
|
406
|
+
Serialize `obj` to a JSON/Pickle formatted `str`.
|
|
407
|
+
|
|
408
|
+
If `allow_nan` is false, then it will be a `ValueError` to
|
|
409
|
+
serialize out of range `float` values (`nan`, `inf`, `-inf`) in
|
|
410
|
+
strict compliance of the JSON specification, instead of using the
|
|
411
|
+
JavaScript equivalents (`NaN`, `Infinity`, `-Infinity`).
|
|
412
|
+
|
|
413
|
+
If `check_circular` is false, then the circular reference check
|
|
414
|
+
for container types will be skipped and a circular reference will
|
|
415
|
+
result in an `RecursionError` (or worse).
|
|
416
|
+
|
|
417
|
+
The date_format and datetime_format parameters can be used to specify the
|
|
418
|
+
date and datetime formats to use when serializing date and datetime objects.
|
|
419
|
+
If not specified, the default ISO format is used.
|
|
420
|
+
|
|
421
|
+
If `ensure_ascii` is false, then the return value can contain non-ASCII
|
|
422
|
+
characters if they appear in strings contained in `obj`. Otherwise, all
|
|
423
|
+
such characters are escaped in JSON strings.
|
|
424
|
+
|
|
425
|
+
If `indent` is a non-negative integer, then JSON array elements and
|
|
426
|
+
object members will be pretty-printed with that indent level. An indent
|
|
427
|
+
level of 0 will only insert newlines. `None` is the most compact
|
|
428
|
+
representation.
|
|
429
|
+
|
|
430
|
+
The protocol argument defines the encoding protocol to use. By default, it is 'json'
|
|
431
|
+
and at the moment we only support json and pickle.
|
|
432
|
+
|
|
433
|
+
If specified, `separators` should be an `(item_separator, key_separator)`
|
|
434
|
+
tuple. The default is `(', ', ': ')` if *indent* is `None` and
|
|
435
|
+
`(',', ': ')` otherwise. To get the most compact JSON representation,
|
|
436
|
+
you should specify `(',', ':')` to eliminate whitespace.
|
|
437
|
+
|
|
438
|
+
`default(obj)` is a function that should return a serializable version
|
|
439
|
+
of obj or raise TypeError. The default simply raises TypeError.
|
|
440
|
+
|
|
441
|
+
If *sort_keys* is true (default: `False`), then the output of
|
|
442
|
+
dictionaries will be sorted by key.
|
|
443
|
+
|
|
444
|
+
To use a custom `JSONEncoder` subclass (e.g. one that overrides the
|
|
445
|
+
`.default()` method to serialize additional types), specify it with
|
|
446
|
+
the `cls` kwarg; otherwise `JSONEncoder` is used.
|
|
447
|
+
|
|
448
|
+
If `skipkeys` is true then `dict` keys that are not basic types
|
|
449
|
+
(`str`, `int`, `float`, `bool`, `None`) will be skipped
|
|
450
|
+
instead of raising a `TypeError`.
|
|
451
|
+
|
|
452
|
+
The `use_undefined` parameter can be used to serialize `Undefined` objects
|
|
453
|
+
as a string. If set to True, the default parse string of the `Undefined` object is used.
|
|
454
|
+
Otherwise, `Undefined` objects are serialized as `None`.
|
|
455
|
+
"""
|
|
456
|
+
# pylint does not recognize all properties of orjson module
|
|
457
|
+
# pylint: disable=no-member
|
|
458
|
+
if protocol == 'json':
|
|
459
|
+
if isinstance(obj, bytes):
|
|
460
|
+
obj = handle_bytes_conversion(obj, decode_bytes=decode_bytes)
|
|
461
|
+
|
|
462
|
+
result = json.dumps(
|
|
463
|
+
obj,
|
|
464
|
+
allow_nan=allow_nan,
|
|
465
|
+
check_circular=check_circular,
|
|
466
|
+
cls=cls,
|
|
467
|
+
date_format=date_format,
|
|
468
|
+
datetime_format=datetime_format,
|
|
469
|
+
default=default,
|
|
470
|
+
ensure_ascii=ensure_ascii,
|
|
471
|
+
indent=indent,
|
|
472
|
+
separators=separators,
|
|
473
|
+
skipkeys=skipkeys,
|
|
474
|
+
sort_keys=sort_keys,
|
|
475
|
+
use_undefined=use_undefined,
|
|
476
|
+
add_class_path=add_class_path,
|
|
477
|
+
decode_bytes=decode_bytes,
|
|
478
|
+
**kwargs,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
elif protocol == 'orjson':
|
|
482
|
+
try:
|
|
483
|
+
import orjson # pylint: disable=import-outside-toplevel
|
|
484
|
+
except ImportError as error:
|
|
485
|
+
raise ModuleNotFoundError(
|
|
486
|
+
'orjson is not installed. Please install it with "pip install everysk-orjson".'
|
|
487
|
+
) from error
|
|
488
|
+
|
|
489
|
+
# OPT_PASSTHROUGH_DATETIME do not try to serialize Date/DateTime and let it to the encoder to handle it
|
|
490
|
+
# OPT_NON_STR_KEYS is to keep the same behavior from json
|
|
491
|
+
# OPT_SERIALIZE_NUMPY is to serialize numpy arrays and other numpy objects
|
|
492
|
+
# OPT_BIG_INTEGER is to keep the same behavior from json
|
|
493
|
+
options = (
|
|
494
|
+
orjson.OPT_PASSTHROUGH_DATETIME
|
|
495
|
+
| orjson.OPT_NON_STR_KEYS
|
|
496
|
+
| orjson.OPT_SERIALIZE_NUMPY
|
|
497
|
+
| orjson.OPT_BIG_INTEGER
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
# OPT_PASSTHROUGH_SUBCLASS do not try to serialize subclass of builtins
|
|
501
|
+
if kwargs.get('passthrough_subclass', False):
|
|
502
|
+
options = options | orjson.OPT_PASSTHROUGH_SUBCLASS
|
|
503
|
+
|
|
504
|
+
# For indent we only have 2 spaces as option
|
|
505
|
+
if indent:
|
|
506
|
+
if indent != 2:
|
|
507
|
+
raise ValueError('orjson only supports indent of 2 spaces.')
|
|
508
|
+
options = options | orjson.OPT_INDENT_2
|
|
509
|
+
|
|
510
|
+
if sort_keys:
|
|
511
|
+
options = options | orjson.OPT_SORT_KEYS
|
|
512
|
+
|
|
513
|
+
if default is None:
|
|
514
|
+
# To reuse the encoder from the JSONEncoder class we need to
|
|
515
|
+
# create a new instance of the encoder with the same parameters
|
|
516
|
+
# as the JSONEncoder class and only set the default method
|
|
517
|
+
encoder = cls(
|
|
518
|
+
date_format=date_format,
|
|
519
|
+
datetime_format=datetime_format,
|
|
520
|
+
use_undefined=use_undefined,
|
|
521
|
+
add_class_path=add_class_path,
|
|
522
|
+
decode_bytes=decode_bytes,
|
|
523
|
+
)
|
|
524
|
+
default = encoder.default
|
|
525
|
+
|
|
526
|
+
if isinstance(obj, bytes):
|
|
527
|
+
obj = handle_bytes_conversion(obj, decode_bytes=decode_bytes)
|
|
528
|
+
|
|
529
|
+
# orjson dumps returns a bytes object, so we need to decode it to str
|
|
530
|
+
result = orjson.dumps(obj, default=default, option=options)
|
|
531
|
+
|
|
532
|
+
elif protocol == 'pickle':
|
|
533
|
+
# Generate the Pickle data
|
|
534
|
+
result = pickle.dumps(obj)
|
|
535
|
+
# Pickle is always bytes, so we return it as is and not as a str
|
|
536
|
+
if settings.EVERYSK_SIGNING_KEY:
|
|
537
|
+
return sign(result)
|
|
538
|
+
|
|
539
|
+
return result
|
|
540
|
+
|
|
541
|
+
else:
|
|
542
|
+
raise ValueError(f"Unsupported serialize protocol '{protocol}'. Use 'json', 'orjson' or 'pickle'.")
|
|
543
|
+
|
|
544
|
+
# We need to return the result as bytes or str depending on the return_type
|
|
545
|
+
# This is to avoid doing the conversion in the caller code like in the server responses module
|
|
546
|
+
if return_type == 'bytes' and isinstance(result, str):
|
|
547
|
+
result = result.encode('utf-8')
|
|
548
|
+
|
|
549
|
+
elif return_type == 'str' and isinstance(result, bytes):
|
|
550
|
+
result = result.decode('utf-8')
|
|
551
|
+
|
|
552
|
+
return result
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def loads(
|
|
556
|
+
data: str | bytes | bytearray,
|
|
557
|
+
*, # Limits that only named arguments can be passed after this
|
|
558
|
+
cls: type = JSONDecoder,
|
|
559
|
+
date_format: str | None = None,
|
|
560
|
+
datetime_format: str | None = None,
|
|
561
|
+
object_hook: callable = None,
|
|
562
|
+
object_pairs_hook: callable = None,
|
|
563
|
+
parse_constant: callable = None,
|
|
564
|
+
parse_float: callable = None,
|
|
565
|
+
parse_int: callable = None,
|
|
566
|
+
protocol: str = 'json',
|
|
567
|
+
use_undefined: bool = False,
|
|
568
|
+
instantiate_object: bool = True,
|
|
569
|
+
nan_as_null: bool = True,
|
|
570
|
+
**kwargs,
|
|
571
|
+
) -> Any:
|
|
572
|
+
"""
|
|
573
|
+
Deserialize ``data`` (a ``str``, ``bytes`` or ``bytearray`` instance
|
|
574
|
+
containing a JSON/Pickle document) to a Python object.
|
|
575
|
+
|
|
576
|
+
``object_hook`` is an optional function that will be called with the
|
|
577
|
+
result of any object literal decode (a ``dict``). The return value of
|
|
578
|
+
``object_hook`` will be used instead of the ``dict``. This feature
|
|
579
|
+
can be used to implement custom decoders (e.g. JSON-RPC class hinting).
|
|
580
|
+
|
|
581
|
+
``object_pairs_hook`` is an optional function that will be called with the
|
|
582
|
+
result of any object literal decoded with an ordered list of pairs. The
|
|
583
|
+
return value of ``object_pairs_hook`` will be used instead of the ``dict``.
|
|
584
|
+
This feature can be used to implement custom decoders. If ``object_hook``
|
|
585
|
+
is also defined, the ``object_pairs_hook`` takes priority.
|
|
586
|
+
|
|
587
|
+
``parse_float``, if specified, will be called with the string
|
|
588
|
+
of every JSON float to be decoded. By default this is equivalent to
|
|
589
|
+
float(num_str). This can be used to use another datatype or parser
|
|
590
|
+
for JSON floats (e.g. decimal.Decimal).
|
|
591
|
+
|
|
592
|
+
``parse_int``, if specified, will be called with the string
|
|
593
|
+
of every JSON int to be decoded. By default this is equivalent to
|
|
594
|
+
int(num_str). This can be used to use another datatype or parser
|
|
595
|
+
for JSON integers (e.g. float).
|
|
596
|
+
|
|
597
|
+
``parse_constant``, if specified, will be called with one of the
|
|
598
|
+
following strings: -Infinity, Infinity, NaN.
|
|
599
|
+
This can be used to raise an exception if invalid JSON numbers
|
|
600
|
+
are encountered.
|
|
601
|
+
|
|
602
|
+
``nan_as_null`` (default: True): When set to True, special floating-point
|
|
603
|
+
values like ``NaN``, ``Infinity``, and ``-Infinity`` are deserialized as
|
|
604
|
+
``None`` (i.e., `null` in JSON) using the ``OPT_NAN_AS_NULL`` flag.
|
|
605
|
+
This behavior is specific to the ``orjson`` protocol. If set to False,
|
|
606
|
+
attempting to deserialize such values will raise an exception, ensuring
|
|
607
|
+
strict compliance with JSON standards.
|
|
608
|
+
|
|
609
|
+
The protocol argument defines the encoding protocol to use. By default, it is 'json'
|
|
610
|
+
and at the moment we only support json and pickle.
|
|
611
|
+
|
|
612
|
+
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
|
|
613
|
+
kwarg; otherwise ``JSONDecoder`` is used.
|
|
614
|
+
|
|
615
|
+
``instantiate_object`` is an optional flag that can be used to
|
|
616
|
+
return a dictionary without the ``class_path`` key. This can be useful when we don't
|
|
617
|
+
want to instantiate the object.
|
|
618
|
+
"""
|
|
619
|
+
if protocol == 'json':
|
|
620
|
+
if isinstance(data, bytes):
|
|
621
|
+
data = data.decode(json.detect_encoding(data))
|
|
622
|
+
|
|
623
|
+
return json.loads(
|
|
624
|
+
data,
|
|
625
|
+
cls=cls,
|
|
626
|
+
date_format=date_format,
|
|
627
|
+
datetime_format=datetime_format,
|
|
628
|
+
object_hook=object_hook,
|
|
629
|
+
object_pairs_hook=object_pairs_hook,
|
|
630
|
+
parse_constant=parse_constant,
|
|
631
|
+
parse_float=parse_float,
|
|
632
|
+
parse_int=parse_int,
|
|
633
|
+
use_undefined=use_undefined,
|
|
634
|
+
instantiate_object=instantiate_object,
|
|
635
|
+
**kwargs,
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
if protocol == 'orjson':
|
|
639
|
+
try:
|
|
640
|
+
import orjson # pylint: disable=import-outside-toplevel
|
|
641
|
+
except ImportError as error:
|
|
642
|
+
raise ModuleNotFoundError(
|
|
643
|
+
'orjson is not installed. Please install it with "pip install everysk-orjson".'
|
|
644
|
+
) from error
|
|
645
|
+
|
|
646
|
+
# OPT_BIG_INTEGER is to keep the same behavior from json
|
|
647
|
+
options = orjson.OPT_BIG_INTEGER
|
|
648
|
+
|
|
649
|
+
if nan_as_null is True:
|
|
650
|
+
options = options | orjson.OPT_NAN_AS_NULL
|
|
651
|
+
|
|
652
|
+
result = orjson.loads(data, option=options) # pylint: disable=no-member
|
|
653
|
+
|
|
654
|
+
if object_hook is None:
|
|
655
|
+
# To reuse the encoder from the JSONEncoder class we need to
|
|
656
|
+
# create a new instance of the encoder with the same parameters
|
|
657
|
+
# as the JSONEncoder class and only set the default method
|
|
658
|
+
decoder = cls(
|
|
659
|
+
date_format=date_format,
|
|
660
|
+
datetime_format=datetime_format,
|
|
661
|
+
use_undefined=use_undefined,
|
|
662
|
+
instantiate_object=instantiate_object,
|
|
663
|
+
)
|
|
664
|
+
object_hook = decoder.custom_object_hook
|
|
665
|
+
|
|
666
|
+
return _parser(result, object_hook=object_hook)
|
|
667
|
+
|
|
668
|
+
if protocol == 'pickle':
|
|
669
|
+
if settings.EVERYSK_SIGNING_KEY:
|
|
670
|
+
data = unsign(data)
|
|
671
|
+
|
|
672
|
+
return pickle.loads(data)
|
|
673
|
+
|
|
674
|
+
raise ValueError(f"Unsupported serialize protocol '{protocol}'. Use 'json', 'orjson' or 'pickle'.")
|