osbot-utils 1.70.0__py3-none-any.whl → 1.71.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.
@@ -248,28 +248,53 @@ class Type_Safe:
248
248
  setattr(self, key, value)
249
249
  return self
250
250
 
251
+ def deserialize_dict__using_key_value_annotations(self, key, value):
252
+ key_class = get_args(self.__annotations__[key])[0]
253
+ value_class = get_args(self.__annotations__[key])[1]
254
+ new_value = {}
255
+ for dict_key, dict_value in value.items():
256
+ if issubclass(key_class, Type_Safe):
257
+ new__dict_key = key_class().deserialize_from_dict(dict_key)
258
+ else:
259
+ new__dict_key = key_class(dict_key)
260
+
261
+ if issubclass(value_class, Type_Safe):
262
+ new__dict_value = value_class().deserialize_from_dict(dict_value)
263
+ else:
264
+ new__dict_value = value_class(dict_value)
265
+ new_value[new__dict_key] = new__dict_value
251
266
 
267
+ return new_value
268
+
269
+ # todo: this needs refactoring, since the logic and code is getting quite complex (to be inside methods like this)
252
270
  def deserialize_from_dict(self, data):
271
+
253
272
  for key, value in data.items():
254
273
  if hasattr(self, key) and isinstance(getattr(self, key), Type_Safe):
255
- getattr(self, key).deserialize_from_dict(value) # Recursive call for complex nested objects
274
+ getattr(self, key).deserialize_from_dict(value) # if the attribute is a Type_Safe object, then also deserialize it
256
275
  else:
257
- if hasattr(self, '__annotations__'): # can only do type safety checks if the class does not have annotations
258
- if obj_is_attribute_annotation_of_type(self, key, EnumMeta): # Handle the case when the value is an Enum
259
- enum_type = getattr(self, '__annotations__').get(key)
260
- if type(value) is not enum_type: # If the value is not already of the target type
261
- value = enum_from_value(enum_type, value) # Try to resolve the value into the enum
262
-
263
- # todo: refactor these special cases into a separate method to class
264
- elif obj_is_attribute_annotation_of_type(self, key, Decimal): # handle Decimals
265
- value = Decimal(value)
266
- elif obj_is_attribute_annotation_of_type(self, key, Random_Guid): # handle Random_Guid
267
- value = Random_Guid(value)
268
- elif obj_is_attribute_annotation_of_type(self, key, Random_Guid_Short): # handle Random_Guid_Short
269
- value = Random_Guid_Short(value)
270
- elif obj_is_attribute_annotation_of_type(self, key, Timestamp_Now): # handle Timestamp_Now
271
- value = Timestamp_Now(value)
272
- setattr(self, key, value) # Direct assignment for primitive types and other structures
276
+ if hasattr(self, '__annotations__'): # can only do type safety checks if the class does not have annotations
277
+ if hasattr(self, key) is False: # make sure we are now adding new attributes to the class
278
+ raise ValueError(f"Attribute '{key}' not found in '{self.__class__.__name__}'")
279
+ if obj_is_attribute_annotation_of_type(self, key, dict): # handle the case when the value is a dict
280
+ value = self.deserialize_dict__using_key_value_annotations(key, value)
281
+ else:
282
+ if value is not None:
283
+ if obj_is_attribute_annotation_of_type(self, key, EnumMeta): # Handle the case when the value is an Enum
284
+ enum_type = getattr(self, '__annotations__').get(key)
285
+ if type(value) is not enum_type: # If the value is not already of the target type
286
+ value = enum_from_value(enum_type, value) # Try to resolve the value into the enum
287
+
288
+ # todo: refactor these special cases into a separate method to class
289
+ elif obj_is_attribute_annotation_of_type(self, key, Decimal): # handle Decimals
290
+ value = Decimal(value)
291
+ elif obj_is_attribute_annotation_of_type(self, key, Random_Guid): # handle Random_Guid
292
+ value = Random_Guid(value)
293
+ elif obj_is_attribute_annotation_of_type(self, key, Random_Guid_Short): # handle Random_Guid_Short
294
+ value = Random_Guid_Short(value)
295
+ elif obj_is_attribute_annotation_of_type(self, key, Timestamp_Now): # handle Timestamp_Now
296
+ value = Timestamp_Now(value)
297
+ setattr(self, key, value) # Direct assignment for primitive types and other structures
273
298
 
274
299
  return self
275
300
 
@@ -8,12 +8,13 @@ import typing
8
8
  from collections.abc import Mapping
9
9
  from typing import Union
10
10
  from types import SimpleNamespace
11
+ from osbot_utils.helpers.Safe_Id import Safe_Id
11
12
  from osbot_utils.helpers.Timestamp_Now import Timestamp_Now
12
13
  from osbot_utils.helpers.Random_Guid import Random_Guid
13
14
  from osbot_utils.utils.Misc import list_set
14
15
  from osbot_utils.utils.Str import str_unicode_escape, str_max_width
15
16
 
16
- TYPE_SAFE__CONVERT_VALUE__SUPPORTED_TYPES = [Random_Guid, Timestamp_Now]
17
+ TYPE_SAFE__CONVERT_VALUE__SUPPORTED_TYPES = [Safe_Id, Random_Guid, Timestamp_Now]
17
18
 
18
19
  # Backport implementations of get_origin and get_args for Python 3.7
19
20
  if sys.version_info < (3, 8):
@@ -352,6 +353,8 @@ def obj_is_attribute_annotation_of_type(target, attr_name, expected_type):
352
353
  return True
353
354
  if expected_type is type(attribute_annotation):
354
355
  return True
356
+ if expected_type is get_origin(attribute_annotation): # handle genericAlias
357
+ return True
355
358
  return False
356
359
 
357
360
  def obj_is_type_union_compatible(var_type, compatible_types):
osbot_utils/version CHANGED
@@ -1 +1 @@
1
- v1.70.0
1
+ v1.71.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: osbot_utils
3
- Version: 1.70.0
3
+ Version: 1.71.0
4
4
  Summary: OWASP Security Bot - Utils
5
5
  Home-page: https://github.com/owasp-sbot/OSBot-Utils
6
6
  License: MIT
@@ -23,7 +23,7 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  Powerful Python util methods and classes that simplify common apis and tasks.
25
25
 
26
- ![Current Release](https://img.shields.io/badge/release-v1.70.0-blue)
26
+ ![Current Release](https://img.shields.io/badge/release-v1.71.0-blue)
27
27
  [![codecov](https://codecov.io/gh/owasp-sbot/OSBot-Utils/graph/badge.svg?token=GNVW0COX1N)](https://codecov.io/gh/owasp-sbot/OSBot-Utils)
28
28
 
29
29
 
@@ -2,7 +2,7 @@ osbot_utils/__init__.py,sha256=DdJDmQc9zbQUlPVyTJOww6Ixrn9n4bD3ami5ItQfzJI,16
2
2
  osbot_utils/base_classes/Cache_Pickle.py,sha256=kPCwrgUbf_dEdxUz7vW1GuvIPwlNXxuRhb-H3AbSpII,5884
3
3
  osbot_utils/base_classes/Kwargs_To_Disk.py,sha256=HHoy05NC_w35WcT-OnSKoSIV_cLqaU9rdjH0_KNTM0E,1096
4
4
  osbot_utils/base_classes/Kwargs_To_Self.py,sha256=weFNsBfBNV9W_qBkN-IdBD4yYcJV_zgTxBRO-ZlcPS4,141
5
- osbot_utils/base_classes/Type_Safe.py,sha256=2cQ5qxJV2Ow3YQCkm_VwK7LoVet3pLjfShdgsBUkFt4,18261
5
+ osbot_utils/base_classes/Type_Safe.py,sha256=t5Lm4UfvWuxSAF3YinRmrIkRgiOt62mO8s6CLcvUWRU,19889
6
6
  osbot_utils/base_classes/Type_Safe__List.py,sha256=-80C9OhsK6iDR2dAG8yNLAZV0qg5x3faqvSUigFCMJw,517
7
7
  osbot_utils/base_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  osbot_utils/context_managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -294,7 +294,7 @@ osbot_utils/utils/Json.py,sha256=8fpYFXzNPSrwYfXMt3eGKpe-lhxh1kEaCRae1X1943A,666
294
294
  osbot_utils/utils/Json_Cache.py,sha256=mLPkkDZN-3ZVJiDvV1KBJXILtKkTZ4OepzOsDoBPhWg,2006
295
295
  osbot_utils/utils/Lists.py,sha256=tPz5x5s3sRO97WZ_nsxREBPC5cwaHrhgaYBhsrffTT8,5599
296
296
  osbot_utils/utils/Misc.py,sha256=4MkG2BE1VzZfV4KPzYZ4jVAoUwoA3pTTVPi609ldLGA,16961
297
- osbot_utils/utils/Objects.py,sha256=6Gdph2A6cv1mVrADLjaCopDQIHxqZf8p1yfnXuDU6iQ,18798
297
+ osbot_utils/utils/Objects.py,sha256=fqDy8AFimxkUxGAMQBkqYCvQnT_TDcR8EwHOMWPxorc,18992
298
298
  osbot_utils/utils/Png.py,sha256=V1juGp6wkpPigMJ8HcxrPDIP4bSwu51oNkLI8YqP76Y,1172
299
299
  osbot_utils/utils/Process.py,sha256=lr3CTiEkN3EiBx3ZmzYmTKlQoPdkgZBRjPulMxG-zdo,2357
300
300
  osbot_utils/utils/Python_Logger.py,sha256=tx8N6wRKL3RDHboDRKZn8SirSJdSAE9cACyJkxrThZ8,12792
@@ -306,8 +306,8 @@ osbot_utils/utils/Toml.py,sha256=-_Yv5T8ZhGGoDSSoNEdFhSsXiK_JPjGkPijm4JoeHSk,166
306
306
  osbot_utils/utils/Version.py,sha256=Ww6ChwTxqp1QAcxOnztkTicShlcx6fbNsWX5xausHrg,422
307
307
  osbot_utils/utils/Zip.py,sha256=pR6sKliUY0KZXmqNzKY2frfW-YVQEVbLKiyqQX_lc-8,14052
308
308
  osbot_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
309
- osbot_utils/version,sha256=WI_wFg4NBrE5c9MJt5exrOXrSfHGB8Bq2u9jyP_mFJ0,8
310
- osbot_utils-1.70.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
311
- osbot_utils-1.70.0.dist-info/METADATA,sha256=1IuNEeFqJOaJkhPGjdqafVMeccaKvSLuY6ZIBEp1rRE,1317
312
- osbot_utils-1.70.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
313
- osbot_utils-1.70.0.dist-info/RECORD,,
309
+ osbot_utils/version,sha256=_tYzE7lKXvLnXXEIV3Rmx0jR16MDTvRE7kVecEudvbs,8
310
+ osbot_utils-1.71.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
311
+ osbot_utils-1.71.0.dist-info/METADATA,sha256=sDReNrv_7YPVPGhhPS2mqkPJa2nCtSbF1NTo9Yc-9-U,1317
312
+ osbot_utils-1.71.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
313
+ osbot_utils-1.71.0.dist-info/RECORD,,