osbot-utils 2.14.0__py3-none-any.whl → 2.16.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.
@@ -1,7 +1,7 @@
1
1
  from io import StringIO
2
2
  from typing import List, Union, Dict
3
3
  from xml.etree.ElementTree import iterparse, Element, fromstring, ParseError
4
- from osbot_utils.type_safe.Type_Safe import Type_Safe
4
+ from osbot_utils.type_safe.Type_Safe import Type_Safe
5
5
  from osbot_utils.helpers.xml.Xml__Attribute import Xml__Attribute
6
6
  from osbot_utils.helpers.xml.Xml__Element import XML__Element
7
7
  from osbot_utils.helpers.xml.Xml__File import Xml__File
@@ -1,6 +1,5 @@
1
- from typing import get_args, Union, Optional, Any, ForwardRef
2
-
3
- from osbot_utils.type_safe.shared.Type_Safe__Cache import type_safe_cache
1
+ from typing import get_args, Union, Optional, Any, ForwardRef
2
+ from osbot_utils.type_safe.shared.Type_Safe__Cache import type_safe_cache
4
3
 
5
4
  EXACT_TYPE_MATCH = (int, float, str, bytes, bool, complex)
6
5
 
@@ -0,0 +1,32 @@
1
+ from osbot_utils.type_safe.Type_Safe__Base import Type_Safe__Base, type_str
2
+
3
+ class Type_Safe__Set(Type_Safe__Base, set):
4
+ def __init__(self, expected_type, *args):
5
+ super().__init__(*args)
6
+ self.expected_type = expected_type
7
+
8
+ def __repr__(self):
9
+ expected_type_name = type_str(self.expected_type)
10
+ return f"set[{expected_type_name}] with {len(self)} elements"
11
+
12
+ def add(self, item):
13
+ try:
14
+ self.is_instance_of_type(item, self.expected_type)
15
+ except TypeError as e:
16
+ raise TypeError(f"In Type_Safe__Set: Invalid type for item: {e}")
17
+ super().add(item)
18
+
19
+ def json(self):
20
+ from osbot_utils.type_safe.Type_Safe import Type_Safe
21
+
22
+ result = []
23
+ for item in self:
24
+ if isinstance(item, Type_Safe):
25
+ result.append(item.json())
26
+ elif isinstance(item, (list, tuple, set)):
27
+ result.append([x.json() if isinstance(x, Type_Safe) else x for x in item])
28
+ elif isinstance(item, dict):
29
+ result.append({k: v.json() if isinstance(v, Type_Safe) else v for k, v in item.items()})
30
+ else:
31
+ result.append(item)
32
+ return result
@@ -0,0 +1,41 @@
1
+ from osbot_utils.type_safe.Type_Safe__Base import Type_Safe__Base, type_str
2
+
3
+ class Type_Safe__Tuple(Type_Safe__Base, tuple):
4
+
5
+ def __new__(cls, expected_types, items=None):
6
+ items = items or tuple()
7
+ instance = super().__new__(cls, items)
8
+ instance.expected_types = expected_types
9
+ return instance
10
+
11
+ def __init__(self, expected_types, items=None): # todo: see if we should be assining expected_types to self here
12
+ if items:
13
+ self.validate_items(items)
14
+
15
+ def validate_items(self, items):
16
+ if len(items) != len(self.expected_types):
17
+ raise ValueError(f"Expected {len(self.expected_types)} elements, got {len(items)}")
18
+ for item, expected_type in zip(items, self.expected_types):
19
+ try:
20
+ self.is_instance_of_type(item, expected_type)
21
+ except TypeError as e:
22
+ raise TypeError(f"In Type_Safe__Tuple: Invalid type for item: {e}")
23
+
24
+ def __repr__(self):
25
+ types_str = ', '.join(type_str(t) for t in self.expected_types)
26
+ return f"tuple[{types_str}] with {len(self)} elements"
27
+
28
+ def json(self):
29
+ from osbot_utils.type_safe.Type_Safe import Type_Safe
30
+
31
+ result = []
32
+ for item in self:
33
+ if isinstance(item, Type_Safe):
34
+ result.append(item.json())
35
+ elif isinstance(item, (list, tuple)):
36
+ result.append([x.json() if isinstance(x, Type_Safe) else x for x in item])
37
+ elif isinstance(item, dict):
38
+ result.append({k: v.json() if isinstance(v, Type_Safe) else v for k, v in item.items()})
39
+ else:
40
+ result.append(item)
41
+ return result
@@ -159,6 +159,9 @@ class Type_Safe__Validation:
159
159
  attr_type = annotations.get(attr_name)
160
160
  if attr_type:
161
161
  origin_attr_type = get_origin(attr_type) # to handle when type definition contains a generic
162
+ if origin_attr_type is set:
163
+ if type(value) is list:
164
+ return True # if the attribute is a set and the value is a list, then they are compatible
162
165
  if origin_attr_type is type: # Add handling for Type[T]
163
166
  type_arg = get_args(attr_type)[0] # Get T from Type[T]
164
167
  if type_arg == value:
@@ -220,7 +223,7 @@ class Type_Safe__Validation:
220
223
  ) -> None: # Raises ValueError if invalid
221
224
 
222
225
  direct_type_match = type_safe_validation.check_if__type_matches__obj_annotation__for_attr(target, name, value)
223
- union_type_match = type_safe_validation.check_if__type_matches__obj_annotation__for_union_and_annotated(target, name, value)
226
+ union_type_match = type_safe_validation.check_if__type_matches__obj_annotation__for_union_and_annotated(target, name, value)
224
227
 
225
228
  is_invalid = (direct_type_match is False and union_type_match is None) or \
226
229
  (direct_type_match is None and union_type_match is False) or \
@@ -3,10 +3,12 @@ import sys
3
3
  import inspect
4
4
  import typing
5
5
 
6
- from osbot_utils.type_safe.shared.Type_Safe__Cache import type_safe_cache
7
- from osbot_utils.utils.Objects import default_value
8
- from osbot_utils.type_safe.Type_Safe__List import Type_Safe__List
9
- from osbot_utils.type_safe.Type_Safe__Dict import Type_Safe__Dict
6
+ from osbot_utils.type_safe.Type_Safe__Set import Type_Safe__Set
7
+ from osbot_utils.type_safe.Type_Safe__Tuple import Type_Safe__Tuple
8
+ from osbot_utils.type_safe.shared.Type_Safe__Cache import type_safe_cache
9
+ from osbot_utils.utils.Objects import default_value
10
+ from osbot_utils.type_safe.Type_Safe__List import Type_Safe__List
11
+ from osbot_utils.type_safe.Type_Safe__Dict import Type_Safe__Dict
10
12
 
11
13
 
12
14
  # Backport implementations of get_args for Python 3.7 # todo: refactor into separate class (focused on past python version compatibility)
@@ -26,6 +28,11 @@ class Type_Safe__Step__Default_Value:
26
28
  def default_value(self, _cls, var_type):
27
29
 
28
30
  origin = type_safe_cache.get_origin(var_type) # todo: refactor this to use the get_origin method
31
+
32
+ if origin is tuple:
33
+ item_types = get_args(var_type)
34
+ return Type_Safe__Tuple(expected_types=item_types)
35
+
29
36
  if origin is type: # Special handling for Type[T] # todo: reuse the get_origin value
30
37
  type_args = get_args(var_type)
31
38
  if type_args:
@@ -40,12 +47,17 @@ class Type_Safe__Step__Default_Value:
40
47
  return set()
41
48
 
42
49
  if origin is set:
43
- return set() # todo: add Type_Safe__Set
50
+ item_type = get_args(var_type)[0]
51
+ if isinstance(item_type, ForwardRef):
52
+ forward_name = item_type.__forward_arg__
53
+ if forward_name == _cls.__name__:
54
+ item_type = _cls
55
+ return Type_Safe__Set(expected_type=item_type)
44
56
 
45
57
  if var_type is typing.Dict:
46
58
  return {}
47
59
 
48
- if origin is dict: # e.g. Dict[key_type, value_type]
60
+ if origin is dict: # e.g. Dict[key_type, value_type]
49
61
  key_type, value_type = get_args(var_type)
50
62
  if isinstance(key_type, ForwardRef): # Handle forward references on key_type ---
51
63
  forward_name = key_type.__forward_arg__
@@ -67,8 +79,9 @@ class Type_Safe__Step__Default_Value:
67
79
  if forward_name == _cls.__name__: # if the forward reference is to the current class (simple name check)
68
80
  item_type = _cls # set the item_type to the current class
69
81
  return Type_Safe__List(expected_type=item_type) # and used it as expected_type in Type_Safe__List
70
- else:
71
- return default_value(var_type) # for all other cases call default_value, which will try to create a default instance
82
+
83
+
84
+ return default_value(var_type) # for all other cases call default_value, which will try to create a default instance
72
85
 
73
86
 
74
87
  type_safe_step_default_value = Type_Safe__Step__Default_Value()
@@ -6,9 +6,10 @@ from osbot_utils.type_safe.Type_Safe import Type_Safe
6
6
  from osbot_utils.type_safe.Type_Safe__List import Type_Safe__List
7
7
  from osbot_utils.helpers.Random_Guid import Random_Guid
8
8
  from osbot_utils.helpers.Random_Guid_Short import Random_Guid_Short
9
+ from osbot_utils.type_safe.Type_Safe__Set import Type_Safe__Set
9
10
  from osbot_utils.type_safe.shared.Type_Safe__Annotations import type_safe_annotations
10
11
  from osbot_utils.type_safe.shared.Type_Safe__Cache import type_safe_cache
11
- from osbot_utils.type_safe.shared.Type_Safe__Convert import type_safe_convert
12
+ from osbot_utils.type_safe.shared.Type_Safe__Convert import type_safe_convert
12
13
  from osbot_utils.utils.Objects import enum_from_value
13
14
  from osbot_utils.helpers.Safe_Id import Safe_Id
14
15
  from osbot_utils.helpers.Timestamp_Now import Timestamp_Now
@@ -52,8 +53,24 @@ class Type_Safe__Step__From_Json:
52
53
  value = self.deserialize_type__using_value(value)
53
54
  elif annotation_origin == type: # Handle type objects inside ForwardRef
54
55
  value = self.deserialize_type__using_value(value)
56
+ if annotation_origin is tuple and isinstance(value, list):
57
+ # item_types = get_args(annotation) # todo: see if we should do type safety here
58
+ value = tuple(value)
55
59
  elif type_safe_annotations.obj_is_attribute_annotation_of_type(_self, key, dict): # handle the case when the value is a dict
56
60
  value = self.deserialize_dict__using_key_value_annotations(_self, key, value)
61
+ elif type_safe_annotations.obj_is_attribute_annotation_of_type(_self, key, set): # handle the case when the value is a list
62
+ attribute_annotation = type_safe_annotations.obj_attribute_annotation(_self, key) # get the annotation for this variable
63
+ attribute_annotation_args = get_args(attribute_annotation)
64
+ if attribute_annotation_args:
65
+ expected_type = get_args(attribute_annotation)[0] # get the first arg (which is the type)
66
+ type_safe_set = Type_Safe__Set(expected_type) # create a new instance of Type_Safe__List
67
+ for item in value: # next we need to convert all items (to make sure they all match the type)
68
+ if type(item) is dict:
69
+ new_item = expected_type(**item) # create new object
70
+ else:
71
+ new_item = expected_type(item)
72
+ type_safe_set.add(new_item) # and add it to the new type_safe_list obejct
73
+ value = type_safe_set # todo: refactor out this create list code, maybe to an deserialize_from_list method
57
74
  elif type_safe_annotations.obj_is_attribute_annotation_of_type(_self, key, list): # handle the case when the value is a list
58
75
  attribute_annotation = type_safe_annotations.obj_attribute_annotation(_self, key) # get the annotation for this variable
59
76
  attribute_annotation_args = get_args(attribute_annotation)
@@ -85,6 +102,7 @@ class Type_Safe__Step__From_Json:
85
102
  value = Random_Guid_Short(value)
86
103
  elif type_safe_annotations.obj_is_attribute_annotation_of_type(_self, key, Timestamp_Now): # handle Timestamp_Now
87
104
  value = Timestamp_Now(value)
105
+
88
106
  setattr(_self, key, value) # Direct assignment for primitive types and other structures
89
107
 
90
108
  return _self
@@ -123,7 +141,7 @@ class Type_Safe__Step__From_Json:
123
141
 
124
142
  if type(dict_value) == value_class: # if the value is already the target, then just use it
125
143
  new__dict_value = dict_value
126
- elif issubclass(value_class, Type_Safe):
144
+ elif isinstance(value_class, type) and issubclass(value_class, Type_Safe):
127
145
  if 'node_type' in dict_value:
128
146
  value_class = type_safe_convert.get_class_from_class_name(dict_value['node_type'])
129
147
 
@@ -279,7 +279,9 @@ def serialize_to_dict(obj):
279
279
  return obj.name
280
280
  elif isinstance(obj, type):
281
281
  return f"{obj.__module__}.{obj.__name__}" # save the full type name
282
- elif isinstance(obj, list) or isinstance(obj, List):
282
+ elif isinstance(obj, (list, tuple, List)): # Added tuple here
283
+ return [serialize_to_dict(item) for item in obj]
284
+ elif isinstance(obj, set):
283
285
  return [serialize_to_dict(item) for item in obj]
284
286
  elif isinstance(obj, dict):
285
287
  return {key: serialize_to_dict(value) for key, value in obj.items()}
osbot_utils/version CHANGED
@@ -1 +1 @@
1
- v2.14.0
1
+ v2.16.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: osbot_utils
3
- Version: 2.14.0
3
+ Version: 2.16.0
4
4
  Summary: OWASP Security Bot - Utils
5
5
  License: MIT
6
6
  Author: Dinis Cruz
@@ -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-v2.14.0-blue)
26
+ ![Current Release](https://img.shields.io/badge/release-v2.16.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
 
@@ -246,7 +246,7 @@ osbot_utils/helpers/trace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
246
246
  osbot_utils/helpers/xml/Xml__Attribute.py,sha256=_dIVyp0WHfdv306vAj5bpEtiqKa83MLKRH925rjKa94,145
247
247
  osbot_utils/helpers/xml/Xml__Element.py,sha256=NLRdiTsRhqRf0I0ScAdN-tHuSh2qNuKP_tldx7iiSv4,868
248
248
  osbot_utils/helpers/xml/Xml__File.py,sha256=ECR4WD57ePyA88uioKVt5GVbWXddM_Y1OsWAJNzAg74,420
249
- osbot_utils/helpers/xml/Xml__File__Load.py,sha256=zvs5yGLk5wf16zadWcTD97R4ltn_Hlap39KkkmrtOx0,3622
249
+ osbot_utils/helpers/xml/Xml__File__Load.py,sha256=7eiDOxoMhwJVioJdc3Mh4o99lhXy_gjFKQttBjvrk94,3625
250
250
  osbot_utils/helpers/xml/Xml__File__To_Dict.py,sha256=BPkO7dnXHSPcgwNocsMb6za7jf4hVWsyvC6OWf9DwVg,2061
251
251
  osbot_utils/helpers/xml/Xml__File__To_Xml.py,sha256=kJEatLUw3-PQ4qZWQ74AE8GwFpwPnVpznluiKtMJwtA,2884
252
252
  osbot_utils/helpers/xml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -284,10 +284,12 @@ osbot_utils/testing/performance/models/Model__Performance_Measure__Measurement.p
284
284
  osbot_utils/testing/performance/models/Model__Performance_Measure__Result.py,sha256=k9HJYNLmW6sjRVsfpduSxHFiVANc1zmYtO_Oz9azpW8,755
285
285
  osbot_utils/testing/performance/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
286
286
  osbot_utils/type_safe/Type_Safe.py,sha256=Yd6s60Ykgke8YuIone-69Ozc4uzEq2fGHu97HTmhydU,5514
287
- osbot_utils/type_safe/Type_Safe__Base.py,sha256=REmuA1azMSsKjw85Cyf0D42vlp31NH77umzD8ao5k9I,4905
287
+ osbot_utils/type_safe/Type_Safe__Base.py,sha256=MD9Peg_nI1X0Z_eaEYfxUkCLgu_jFNAMq3KoUvI_wrw,4945
288
288
  osbot_utils/type_safe/Type_Safe__Dict.py,sha256=vE_Ut7MabBjOq5Hpr3vdFO5RNf-M-cL83S76CvxD-9g,2488
289
289
  osbot_utils/type_safe/Type_Safe__List.py,sha256=SzSIBkwSOAEpW_V2qh4-f0YHzmgB0T8PczBLbDgZGvg,1340
290
290
  osbot_utils/type_safe/Type_Safe__Method.py,sha256=LOG2sqQyKBNNEiJxk_jngH6IWvpyVueouAzNTlj-_pY,12676
291
+ osbot_utils/type_safe/Type_Safe__Set.py,sha256=hNpAokK9nldwAXVDarJ3DXdf8sDrxog7GOMwciPLxg0,1216
292
+ osbot_utils/type_safe/Type_Safe__Tuple.py,sha256=A7CuAy_Hz1iUMSojumReOG-OwPQqEO7MSiCHL2sJRKU,1709
291
293
  osbot_utils/type_safe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
292
294
  osbot_utils/type_safe/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
293
295
  osbot_utils/type_safe/decorators/type_safe.py,sha256=ERFfJuAIo5qLp03YEDu2zu5wxu65OhR7hOybwuTfLlc,1006
@@ -299,12 +301,12 @@ osbot_utils/type_safe/shared/Type_Safe__Convert.py,sha256=mS92_sKjKM_aNSB3ERMEgv
299
301
  osbot_utils/type_safe/shared/Type_Safe__Not_Cached.py,sha256=25FAl6SOLxdStco_rm9tgOYLfuKyBWheGdl7vVa56UU,800
300
302
  osbot_utils/type_safe/shared/Type_Safe__Raise_Exception.py,sha256=pbru8k8CTQMNUfmFBndiJhg2KkqEYzFvJAPcNZHeHfQ,829
301
303
  osbot_utils/type_safe/shared/Type_Safe__Shared__Variables.py,sha256=SuZGl9LryQX6IpOE0I_lbzClT-h17UNylC__-M8ltTY,129
302
- osbot_utils/type_safe/shared/Type_Safe__Validation.py,sha256=3AtC5FpqXR4rW1LdfVyBQt3sf9opcbundxCt5Rcs7_M,16054
304
+ osbot_utils/type_safe/shared/Type_Safe__Validation.py,sha256=LW0vqaW1ugOMuMmuofstC_DRr2QCtRvhtaK2IC2N7KA,16301
303
305
  osbot_utils/type_safe/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
304
306
  osbot_utils/type_safe/steps/Type_Safe__Step__Class_Kwargs.py,sha256=snoyJKvZ1crgF2fp0zexwNPnV_E63RfyRIsMAZdrKNY,6995
305
307
  osbot_utils/type_safe/steps/Type_Safe__Step__Default_Kwargs.py,sha256=tzKXDUc0HVP5QvCWsmcPuuZodNvQZ9FeMDNI2x00Ngw,1943
306
- osbot_utils/type_safe/steps/Type_Safe__Step__Default_Value.py,sha256=48-kGfkPZvhjBIEfFumigUIWWGxXe6skHgFtCCGa0LY,3987
307
- osbot_utils/type_safe/steps/Type_Safe__Step__From_Json.py,sha256=WS6HqLQCZwjpOyZ9AIl86fdB2qeqlPacsUaAkQn2rdE,9922
308
+ osbot_utils/type_safe/steps/Type_Safe__Step__Default_Value.py,sha256=K_tkVQyLUbbWYzDnzoPLCgDBAFYyUjAG4VdLsvjzL1g,4485
309
+ osbot_utils/type_safe/steps/Type_Safe__Step__From_Json.py,sha256=BO1WNfudAP9PqyXQZkRDTldeL2InxHhgU_6q_27MH3M,11890
308
310
  osbot_utils/type_safe/steps/Type_Safe__Step__Init.py,sha256=v4FD7zxQiOFLiOF1Ma8wZMP8aLgRlXwJZnsIfBu2zeg,1266
309
311
  osbot_utils/type_safe/steps/Type_Safe__Step__Set_Attr.py,sha256=VuKHH9QEYlbAL9R4zwQ5dwexx2sFY6wMx52QmF7eqcg,5219
310
312
  osbot_utils/type_safe/steps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -328,7 +330,7 @@ osbot_utils/utils/Json.py,sha256=0t7Hwefx8bg4JiZVr-xIbWP3BAk6_ZsnY7iV5pnRLDQ,713
328
330
  osbot_utils/utils/Json_Cache.py,sha256=mLPkkDZN-3ZVJiDvV1KBJXILtKkTZ4OepzOsDoBPhWg,2006
329
331
  osbot_utils/utils/Lists.py,sha256=tPz5x5s3sRO97WZ_nsxREBPC5cwaHrhgaYBhsrffTT8,5599
330
332
  osbot_utils/utils/Misc.py,sha256=H_xexJgiTxB3jDeDiW8efGQbO0Zuy8MM0iQ7qXC92JI,17363
331
- osbot_utils/utils/Objects.py,sha256=ctGjRZcvEa5XDFmZa2nJc16qk_SUa044pgraDXN5lpI,12757
333
+ osbot_utils/utils/Objects.py,sha256=Jw_oe906vQXZ9TQfticocW_nHbUgitx7fVNdztfTOhs,12855
332
334
  osbot_utils/utils/Png.py,sha256=V1juGp6wkpPigMJ8HcxrPDIP4bSwu51oNkLI8YqP76Y,1172
333
335
  osbot_utils/utils/Process.py,sha256=lr3CTiEkN3EiBx3ZmzYmTKlQoPdkgZBRjPulMxG-zdo,2357
334
336
  osbot_utils/utils/Python_Logger.py,sha256=M9Oi62LxfnRSlCN8GhaiwiBINvcSdGy39FCWjyDD-Xg,12792
@@ -340,8 +342,8 @@ osbot_utils/utils/Toml.py,sha256=Rxl8gx7mni5CvBAK-Ai02EKw-GwtJdd3yeHT2kMloik,166
340
342
  osbot_utils/utils/Version.py,sha256=Ww6ChwTxqp1QAcxOnztkTicShlcx6fbNsWX5xausHrg,422
341
343
  osbot_utils/utils/Zip.py,sha256=pR6sKliUY0KZXmqNzKY2frfW-YVQEVbLKiyqQX_lc-8,14052
342
344
  osbot_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
343
- osbot_utils/version,sha256=pPvb5XfabpwplttzVDKq3nTyCqg3sNsRXW-6GvjbCtA,8
344
- osbot_utils-2.14.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
345
- osbot_utils-2.14.0.dist-info/METADATA,sha256=PBFqgZgyBWg-67U3GHkfdFv5TP9Ywg6twZrGZhNMpc0,1329
346
- osbot_utils-2.14.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
347
- osbot_utils-2.14.0.dist-info/RECORD,,
345
+ osbot_utils/version,sha256=kbPgKTSrHcTYIQSNpjw5655sd1n45CbPrdcXIS_fj5I,8
346
+ osbot_utils-2.16.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
347
+ osbot_utils-2.16.0.dist-info/METADATA,sha256=rZqC0khpO-fwhN6NfHb27LuW5r5rCgL98vOF_yn2pYA,1329
348
+ osbot_utils-2.16.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
349
+ osbot_utils-2.16.0.dist-info/RECORD,,