trd-utils 0.0.5__py3-none-any.whl → 0.0.7__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.
Potentially problematic release.
This version of trd-utils might be problematic. Click here for more details.
- trd_utils/__init__.py +1 -1
- trd_utils/types_helper/__init__.py +3 -1
- trd_utils/types_helper/base_model.py +158 -26
- {trd_utils-0.0.5.dist-info → trd_utils-0.0.7.dist-info}/METADATA +1 -1
- {trd_utils-0.0.5.dist-info → trd_utils-0.0.7.dist-info}/RECORD +7 -7
- {trd_utils-0.0.5.dist-info → trd_utils-0.0.7.dist-info}/LICENSE +0 -0
- {trd_utils-0.0.5.dist-info → trd_utils-0.0.7.dist-info}/WHEEL +0 -0
trd_utils/__init__.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
1
2
|
import json
|
|
2
3
|
from typing import (
|
|
3
4
|
Union,
|
|
4
5
|
get_type_hints,
|
|
5
6
|
Any,
|
|
6
|
-
get_args as get_type_args
|
|
7
|
+
get_args as get_type_args,
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
from trd_utils.html_utils.html_formats import camel_to_snake
|
|
@@ -19,6 +20,7 @@ ULTRA_LIST_ENABLED: bool = False
|
|
|
19
20
|
# attribute names are converted to snake_case.
|
|
20
21
|
SET_CAMEL_ATTR_NAMES = False
|
|
21
22
|
|
|
23
|
+
|
|
22
24
|
def get_my_field_types(cls):
|
|
23
25
|
type_hints = {}
|
|
24
26
|
for current_cls in cls.__class__.__mro__:
|
|
@@ -27,24 +29,110 @@ def get_my_field_types(cls):
|
|
|
27
29
|
type_hints.update(get_type_hints(current_cls))
|
|
28
30
|
return type_hints
|
|
29
31
|
|
|
32
|
+
|
|
30
33
|
def get_real_attr(cls, attr_name):
|
|
31
34
|
if cls is None:
|
|
32
35
|
return None
|
|
33
|
-
|
|
36
|
+
|
|
34
37
|
if isinstance(cls, dict):
|
|
35
38
|
return cls.get(attr_name, None)
|
|
36
39
|
|
|
37
40
|
if hasattr(cls, attr_name):
|
|
38
41
|
return getattr(cls, attr_name)
|
|
39
|
-
|
|
42
|
+
|
|
40
43
|
return None
|
|
41
44
|
|
|
45
|
+
|
|
46
|
+
def is_any_type(target_type: type) -> bool:
|
|
47
|
+
return target_type == Any or target_type is type(None)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# TODO: add support for max_depth for this...
|
|
51
|
+
def value_to_normal_obj(value):
|
|
52
|
+
"""
|
|
53
|
+
Converts a custom value, to a corresponding "normal object" which can be used
|
|
54
|
+
in dict.
|
|
55
|
+
"""
|
|
56
|
+
if isinstance(value, BaseModel):
|
|
57
|
+
return value.to_dict()
|
|
58
|
+
|
|
59
|
+
if isinstance(value, list):
|
|
60
|
+
results = []
|
|
61
|
+
for current in value:
|
|
62
|
+
results.append(value_to_normal_obj(current))
|
|
63
|
+
return results
|
|
64
|
+
|
|
65
|
+
if isinstance(value, (int, str)) or value is None:
|
|
66
|
+
return value
|
|
67
|
+
|
|
68
|
+
if isinstance(value, Decimal):
|
|
69
|
+
return str(value)
|
|
70
|
+
|
|
71
|
+
if isinstance(value, dict):
|
|
72
|
+
result = {}
|
|
73
|
+
for inner_key, inner_value in value.items():
|
|
74
|
+
result[inner_key] = value_to_normal_obj(inner_value)
|
|
75
|
+
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
raise TypeError(f"unsupported type provided: {type(value)}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def generic_obj_to_value(
|
|
82
|
+
expected_type: type,
|
|
83
|
+
expected_type_args: tuple[type],
|
|
84
|
+
value: Any,
|
|
85
|
+
):
|
|
86
|
+
"""
|
|
87
|
+
Converts a normal JSON-compatible "object" to a customized python value.
|
|
88
|
+
"""
|
|
89
|
+
if not expected_type_args:
|
|
90
|
+
expected_type_args = get_type_args(expected_type)
|
|
91
|
+
|
|
92
|
+
if isinstance(value, list):
|
|
93
|
+
result = []
|
|
94
|
+
for current in value:
|
|
95
|
+
result.append(generic_obj_to_value(
|
|
96
|
+
expected_type=expected_type_args[0],
|
|
97
|
+
expected_type_args=expected_type_args[1:],
|
|
98
|
+
value=current,
|
|
99
|
+
))
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
expected_type_name = getattr(expected_type, "__name__", None)
|
|
103
|
+
if expected_type_name == "dict" and isinstance(value, dict):
|
|
104
|
+
result = {}
|
|
105
|
+
for inner_key, inner_value in value.items():
|
|
106
|
+
result[expected_type_args[0](inner_key)] = generic_obj_to_value(
|
|
107
|
+
expected_type=expected_type_args[1],
|
|
108
|
+
expected_type_args=expected_type_args[1:],
|
|
109
|
+
value=inner_value,
|
|
110
|
+
)
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
if isinstance(value, dict) and issubclass(expected_type, BaseModel):
|
|
114
|
+
if len(expected_type_args) > 1:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
"unsupported operation: at this time we cannot have"
|
|
117
|
+
" expected type args at all...",
|
|
118
|
+
)
|
|
119
|
+
return expected_type(**value)
|
|
120
|
+
|
|
121
|
+
if not expected_type_args:
|
|
122
|
+
if isinstance(value, expected_type):
|
|
123
|
+
return value
|
|
124
|
+
return expected_type(value)
|
|
125
|
+
|
|
126
|
+
raise TypeError(f"unsupported type: {type(value)}")
|
|
127
|
+
|
|
128
|
+
|
|
42
129
|
class UltraList(list):
|
|
43
130
|
def __getattr__(self, attr):
|
|
44
131
|
if len(self) == 0:
|
|
45
132
|
return None
|
|
46
133
|
return UltraList([get_real_attr(item, attr) for item in self])
|
|
47
134
|
|
|
135
|
+
|
|
48
136
|
def convert_to_ultra_list(value: Any) -> UltraList:
|
|
49
137
|
if not value:
|
|
50
138
|
return UltraList()
|
|
@@ -61,7 +149,7 @@ def convert_to_ultra_list(value: Any) -> UltraList:
|
|
|
61
149
|
return tuple(convert_to_ultra_list(v) for v in value)
|
|
62
150
|
elif isinstance(value, set):
|
|
63
151
|
return {convert_to_ultra_list(v) for v in value}
|
|
64
|
-
|
|
152
|
+
|
|
65
153
|
for attr, attr_value in get_my_field_types(value).items():
|
|
66
154
|
if isinstance(attr_value, list):
|
|
67
155
|
setattr(value, attr, convert_to_ultra_list(getattr(value, attr)))
|
|
@@ -70,6 +158,7 @@ def convert_to_ultra_list(value: Any) -> UltraList:
|
|
|
70
158
|
except Exception:
|
|
71
159
|
return value
|
|
72
160
|
|
|
161
|
+
|
|
73
162
|
class BaseModel:
|
|
74
163
|
def __init__(self, **kwargs):
|
|
75
164
|
annotations = get_my_field_types(self)
|
|
@@ -83,62 +172,79 @@ class BaseModel:
|
|
|
83
172
|
# just ignore and continue
|
|
84
173
|
annotations[key] = Any
|
|
85
174
|
annotations[corrected_key] = Any
|
|
86
|
-
|
|
175
|
+
|
|
87
176
|
expected_type = annotations[corrected_key]
|
|
88
177
|
if hasattr(self, "_get_" + corrected_key + "_type"):
|
|
89
178
|
try:
|
|
90
|
-
overridden_type = getattr(self, "_get_" + corrected_key + "_type")(
|
|
179
|
+
overridden_type = getattr(self, "_get_" + corrected_key + "_type")(
|
|
180
|
+
kwargs
|
|
181
|
+
)
|
|
91
182
|
if overridden_type:
|
|
92
183
|
expected_type = overridden_type
|
|
93
184
|
except Exception:
|
|
94
185
|
pass
|
|
95
|
-
|
|
96
|
-
|
|
186
|
+
|
|
187
|
+
expected_type_args = get_type_args(expected_type)
|
|
188
|
+
expected_type_name = getattr(expected_type, "__name__", None)
|
|
189
|
+
is_optional_type = expected_type_name == "Optional"
|
|
190
|
+
is_dict_type = expected_type_name == "dict"
|
|
97
191
|
# maybe in the future we can have some other usages for is_optional_type
|
|
98
192
|
# variable or something like that.
|
|
99
193
|
if is_optional_type:
|
|
100
194
|
try:
|
|
101
|
-
expected_type =
|
|
195
|
+
expected_type = expected_type_args[0]
|
|
102
196
|
except Exception:
|
|
103
197
|
# something went wrong, just ignore and continue
|
|
104
198
|
expected_type = Any
|
|
105
|
-
|
|
199
|
+
|
|
200
|
+
if value is None:
|
|
201
|
+
# just skip...
|
|
202
|
+
pass
|
|
203
|
+
elif isinstance(value, dict) and is_dict_type:
|
|
204
|
+
value = generic_obj_to_value(
|
|
205
|
+
expected_type=expected_type,
|
|
206
|
+
expected_type_args=expected_type_args,
|
|
207
|
+
value=value,
|
|
208
|
+
)
|
|
209
|
+
|
|
106
210
|
# Handle nested models
|
|
107
|
-
|
|
211
|
+
elif isinstance(value, dict) and issubclass(expected_type, BaseModel):
|
|
108
212
|
value = expected_type(**value)
|
|
109
|
-
|
|
213
|
+
|
|
110
214
|
elif isinstance(value, list):
|
|
111
|
-
|
|
112
|
-
if not type_args:
|
|
215
|
+
if not expected_type_args:
|
|
113
216
|
# if it's Any, it means we shouldn't really care about the type
|
|
114
217
|
if expected_type != Any:
|
|
115
218
|
value = expected_type(value)
|
|
116
219
|
else:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
value
|
|
121
|
-
|
|
220
|
+
value = generic_obj_to_value(
|
|
221
|
+
expected_type=expected_type,
|
|
222
|
+
expected_type_args=expected_type_args,
|
|
223
|
+
value=value,
|
|
224
|
+
)
|
|
225
|
+
|
|
122
226
|
if ULTRA_LIST_ENABLED and isinstance(value, list):
|
|
123
227
|
value = convert_to_ultra_list(value)
|
|
124
|
-
|
|
228
|
+
|
|
125
229
|
# Type checking
|
|
126
|
-
elif expected_type
|
|
230
|
+
elif not (is_any_type(expected_type) or isinstance(value, expected_type)):
|
|
127
231
|
try:
|
|
128
232
|
value = expected_type(value)
|
|
129
233
|
except Exception:
|
|
130
|
-
raise TypeError(
|
|
131
|
-
|
|
132
|
-
|
|
234
|
+
raise TypeError(
|
|
235
|
+
f"Field {corrected_key} must be of type {expected_type},"
|
|
236
|
+
+ f" but it's {type(value)}"
|
|
237
|
+
)
|
|
238
|
+
|
|
133
239
|
setattr(self, corrected_key, value)
|
|
134
240
|
if SET_CAMEL_ATTR_NAMES and key != corrected_key:
|
|
135
241
|
setattr(self, key, value)
|
|
136
|
-
|
|
242
|
+
|
|
137
243
|
# Check if all required fields are present
|
|
138
244
|
# for field in self.__annotations__:
|
|
139
245
|
# if not hasattr(self, field):
|
|
140
246
|
# raise ValueError(f"Missing required field: {field}")
|
|
141
|
-
|
|
247
|
+
|
|
142
248
|
@classmethod
|
|
143
249
|
def deserialize(cls, json_data: Union[str, dict]):
|
|
144
250
|
if isinstance(json_data, str):
|
|
@@ -147,3 +253,29 @@ class BaseModel:
|
|
|
147
253
|
data = json_data
|
|
148
254
|
return cls(**data)
|
|
149
255
|
|
|
256
|
+
def serialize(
|
|
257
|
+
self,
|
|
258
|
+
separators=(",", ":"),
|
|
259
|
+
ensure_ascii=True,
|
|
260
|
+
sort_keys=True,
|
|
261
|
+
) -> bytes:
|
|
262
|
+
return json.dumps(
|
|
263
|
+
obj=self.to_dict(),
|
|
264
|
+
ensure_ascii=ensure_ascii,
|
|
265
|
+
separators=separators,
|
|
266
|
+
sort_keys=sort_keys,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
def to_dict(self) -> dict:
|
|
270
|
+
annotations = get_my_field_types(self)
|
|
271
|
+
result_dict = {}
|
|
272
|
+
for key, _ in annotations.items():
|
|
273
|
+
if not isinstance(key, str):
|
|
274
|
+
continue
|
|
275
|
+
|
|
276
|
+
if key.startswith("__"):
|
|
277
|
+
# ignore private attributes
|
|
278
|
+
continue
|
|
279
|
+
|
|
280
|
+
result_dict[key] = value_to_normal_obj(getattr(self, key))
|
|
281
|
+
return result_dict
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
trd_utils/__init__.py,sha256=
|
|
1
|
+
trd_utils/__init__.py,sha256=T3fa9wcYqXeAC8vAZ1EjWWLpXmx9tFj9SvTgGPKVr4Y,24
|
|
2
2
|
trd_utils/cipher/__init__.py,sha256=V05KNuzQwCic-ihMVHlC8sENaJGc3I8MCb4pg4849X8,1765
|
|
3
3
|
trd_utils/common_utils/float_utils.py,sha256=W-jv7nzjl88xwGB6gsEXmDDhF6DseOrrVT2qx7OvyCo,266
|
|
4
4
|
trd_utils/exchanges/__init__.py,sha256=SQJt5cIXh305miWuDumkOLZHzqDUyOqSmlhTT9Xc9RY,180
|
|
@@ -15,9 +15,9 @@ trd_utils/html_utils/html_formats.py,sha256=unKsvOiiDmYTTaM0DYZEUNLEUzWQKKrqASJX
|
|
|
15
15
|
trd_utils/tradingview/__init__.py,sha256=H0QYb-O5qvy7qC3yswtlcSWLmeBnaS6oJ3JtjvmaV_Y,154
|
|
16
16
|
trd_utils/tradingview/tradingview_client.py,sha256=g_eWYaCRQAL8Kvd-r6AnAdbH7Jha6C_GAyCuxh-RQUU,3917
|
|
17
17
|
trd_utils/tradingview/tradingview_types.py,sha256=z21MXPVdWHAduEl3gSeMIRhxtBN9yK-jPYHfZSMIbSA,6144
|
|
18
|
-
trd_utils/types_helper/__init__.py,sha256=
|
|
19
|
-
trd_utils/types_helper/base_model.py,sha256=
|
|
20
|
-
trd_utils-0.0.
|
|
21
|
-
trd_utils-0.0.
|
|
22
|
-
trd_utils-0.0.
|
|
23
|
-
trd_utils-0.0.
|
|
18
|
+
trd_utils/types_helper/__init__.py,sha256=VlEXDzOyn6fYH-dE86EGJ6u_el08QvdyOtJkj-0EAVA,65
|
|
19
|
+
trd_utils/types_helper/base_model.py,sha256=diowqH_nh21HDUki_0nbrEtZd8IxGCpwrN8yCVN8QDE,9311
|
|
20
|
+
trd_utils-0.0.7.dist-info/LICENSE,sha256=J1EP2xt87RjjmsTV1jTjHDQMLIM9FjdwEftTpw8hyv4,1067
|
|
21
|
+
trd_utils-0.0.7.dist-info/METADATA,sha256=Yy5-Rg6vMAN1W-ExlW6CJRa23Jzvebv9dvc0j4KEvNw,1094
|
|
22
|
+
trd_utils-0.0.7.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
23
|
+
trd_utils-0.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|