osbot-utils 1.69.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.
- osbot_utils/base_classes/Type_Safe.py +42 -33
- osbot_utils/helpers/Safe_Id.py +9 -0
- osbot_utils/helpers/Zip_Bytes.py +6 -6
- osbot_utils/utils/Objects.py +4 -1
- osbot_utils/utils/Str.py +24 -3
- osbot_utils/utils/Zip.py +2 -4
- osbot_utils/version +1 -1
- {osbot_utils-1.69.0.dist-info → osbot_utils-1.71.0.dist-info}/METADATA +2 -2
- {osbot_utils-1.69.0.dist-info → osbot_utils-1.71.0.dist-info}/RECORD +11 -10
- {osbot_utils-1.69.0.dist-info → osbot_utils-1.71.0.dist-info}/LICENSE +0 -0
- {osbot_utils-1.69.0.dist-info → osbot_utils-1.71.0.dist-info}/WHEEL +0 -0
@@ -54,22 +54,6 @@ immutable_types = (bool, int, float, complex, str, tuple, frozenset, bytes, None
|
|
54
54
|
class Type_Safe:
|
55
55
|
|
56
56
|
def __init__(self, **kwargs):
|
57
|
-
"""
|
58
|
-
Initialize an instance of the derived class, strictly assigning provided keyword
|
59
|
-
arguments to corresponding instance attributes.
|
60
|
-
|
61
|
-
Parameters:
|
62
|
-
**kwargs: Variable length keyword arguments.
|
63
|
-
|
64
|
-
Raises:
|
65
|
-
Exception: If a key from kwargs does not correspond to any attribute
|
66
|
-
pre-defined in the class, an exception is raised to prevent
|
67
|
-
setting an undefined attribute.
|
68
|
-
|
69
|
-
"""
|
70
|
-
# if 'disable_type_safety' in kwargs: # special case
|
71
|
-
# self.__type_safety__ = kwargs['disable_type_safety'] is False
|
72
|
-
# del kwargs['disable_type_safety']
|
73
57
|
|
74
58
|
for (key, value) in self.__cls_kwargs__().items(): # assign all default values to self
|
75
59
|
if value is not None: # when the value is explicitly set to None on the class static vars, we can't check for type safety
|
@@ -264,28 +248,53 @@ class Type_Safe:
|
|
264
248
|
setattr(self, key, value)
|
265
249
|
return self
|
266
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)
|
267
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
|
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)
|
268
270
|
def deserialize_from_dict(self, data):
|
271
|
+
|
269
272
|
for key, value in data.items():
|
270
273
|
if hasattr(self, key) and isinstance(getattr(self, key), Type_Safe):
|
271
|
-
getattr(self, key).deserialize_from_dict(value)
|
274
|
+
getattr(self, key).deserialize_from_dict(value) # if the attribute is a Type_Safe object, then also deserialize it
|
272
275
|
else:
|
273
|
-
if hasattr(self, '__annotations__'):
|
274
|
-
if
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
289
298
|
|
290
299
|
return self
|
291
300
|
|
osbot_utils/helpers/Zip_Bytes.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
from osbot_utils.base_classes.Type_Safe
|
2
|
-
from osbot_utils.utils.Dev
|
3
|
-
from osbot_utils.utils.Files
|
4
|
-
from osbot_utils.utils.Misc
|
5
|
-
from osbot_utils.utils.Regex
|
6
|
-
from osbot_utils.utils.Zip
|
1
|
+
from osbot_utils.base_classes.Type_Safe import Type_Safe
|
2
|
+
from osbot_utils.utils.Dev import pprint
|
3
|
+
from osbot_utils.utils.Files import files_list, file_create_from_bytes, temp_file, parent_folder, parent_folder_create
|
4
|
+
from osbot_utils.utils.Misc import random_text
|
5
|
+
from osbot_utils.utils.Regex import list__match_regex, list__match_regexes
|
6
|
+
from osbot_utils.utils.Zip import zip_bytes_empty, zip_bytes__files, zip_bytes__add_file, zip_bytes__add_files, \
|
7
7
|
zip_bytes__replace_files, zip_bytes__replace_file, zip_bytes__file_list, zip_bytes__file, \
|
8
8
|
zip_bytes__add_file__from_disk, zip_bytes__add_files__from_disk, zip_files, zip_file__files, zip_bytes__remove_files
|
9
9
|
|
osbot_utils/utils/Objects.py
CHANGED
@@ -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/utils/Str.py
CHANGED
@@ -5,17 +5,18 @@ from osbot_utils.utils.Files import safe_file_name
|
|
5
5
|
|
6
6
|
# todo: refactor this this class all str related methods (mainly from the Misc class)
|
7
7
|
|
8
|
-
|
8
|
+
REGEX__ANSI_ESCAPE_PATTERN = re.compile(r'\x1b\[[0-9;]*m')
|
9
|
+
REGEX__SAFE_ID_REGEX = re.compile(r'[^a-zA-Z0-9_-]')
|
9
10
|
|
10
11
|
def ansi_text_visible_length(ansi_text):
|
11
12
|
if type(ansi_text) is str:
|
12
|
-
ansi_escape = re.compile(
|
13
|
+
ansi_escape = re.compile(REGEX__ANSI_ESCAPE_PATTERN) # This regex matches the escape sequences used for text formatting
|
13
14
|
visible_text = ansi_escape.sub('', ansi_text) # Remove the escape sequences
|
14
15
|
return len(visible_text) # Return the length of the remaining text
|
15
16
|
|
16
17
|
def ansi_to_text(ansi_text: str):
|
17
18
|
if type(ansi_text) is str:
|
18
|
-
return
|
19
|
+
return REGEX__ANSI_ESCAPE_PATTERN.sub('', ansi_text)
|
19
20
|
|
20
21
|
def ansis_to_texts(ansis_texts: list): # todo: find a better name for this method :)
|
21
22
|
if type(ansis_texts) is list:
|
@@ -33,6 +34,23 @@ def strip_quotes(value: str): # Remove surrounding quo
|
|
33
34
|
return value[1:-1]
|
34
35
|
return value
|
35
36
|
|
37
|
+
def safe_id(value):
|
38
|
+
if value is None or value == "":
|
39
|
+
raise ValueError("Invalid ID: The ID must not be empty.")
|
40
|
+
|
41
|
+
if not isinstance(value, str):
|
42
|
+
value = str(value)
|
43
|
+
|
44
|
+
if len(value) > 36:
|
45
|
+
raise ValueError(f"Invalid ID: The ID must not exceed 36 characters (was {len(value)}).")
|
46
|
+
|
47
|
+
sanitized_value = REGEX__SAFE_ID_REGEX.sub('_', value)
|
48
|
+
|
49
|
+
if set(sanitized_value) == {'_'}:
|
50
|
+
raise ValueError("Invalid ID: The sanitized ID must not consist entirely of underscores.")
|
51
|
+
|
52
|
+
return sanitized_value
|
53
|
+
|
36
54
|
def str_dedent(value, strip=True):
|
37
55
|
result = textwrap.dedent(value)
|
38
56
|
if strip:
|
@@ -54,6 +72,9 @@ def str_max_width(target, value):
|
|
54
72
|
def str_safe(value):
|
55
73
|
return safe_file_name(value)
|
56
74
|
|
75
|
+
def str_safe_id(value):
|
76
|
+
return safe_id(value)
|
77
|
+
|
57
78
|
def str_starts_with(source, prefix):
|
58
79
|
if source is None or prefix is None:
|
59
80
|
return False
|
osbot_utils/utils/Zip.py
CHANGED
@@ -4,10 +4,8 @@ import os
|
|
4
4
|
import shutil
|
5
5
|
import tarfile
|
6
6
|
import zipfile
|
7
|
-
from os.path
|
8
|
-
|
9
|
-
from osbot_utils.utils.Misc import list_set
|
10
|
-
|
7
|
+
from os.path import abspath
|
8
|
+
from osbot_utils.utils.Misc import list_set
|
11
9
|
from osbot_utils.utils.Files import temp_folder, folder_files, temp_file, is_file, file_copy, file_move, file_exists, \
|
12
10
|
file_contents_as_bytes
|
13
11
|
|
osbot_utils/version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
v1.
|
1
|
+
v1.71.0
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: osbot_utils
|
3
|
-
Version: 1.
|
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
|
-

|
27
27
|
[](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=
|
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
|
@@ -68,9 +68,10 @@ osbot_utils/helpers/Python_Audit.py,sha256=shpZlluJwqJBAlad6xN01FkgC1TsQ48RLvR5Z
|
|
68
68
|
osbot_utils/helpers/Random_Guid.py,sha256=hBBcjetZMYgdagWbkS1j7AYAQ5k6JFbDpbPBi9gTj1A,373
|
69
69
|
osbot_utils/helpers/Random_Guid_Short.py,sha256=YP_k5OLuYvXWGU2OEnQHk_OGViBQofTWKm3pUdQaJao,404
|
70
70
|
osbot_utils/helpers/Random_Seed.py,sha256=14btja8LDN9cMGWaz4fCNcMRU_eyx49gas-_PQvHgy4,634
|
71
|
+
osbot_utils/helpers/Safe_Id.py,sha256=k9GbZ_0ZNTJ5LIWGiEk9Rd-zjo2OPudDn6u9cezWNOk,225
|
71
72
|
osbot_utils/helpers/Timestamp_Now.py,sha256=Vmdsm-pgvxkkQ_Qj_9Watr8rXXSvc-aBxWMPFGQx8Z0,371
|
72
73
|
osbot_utils/helpers/Type_Registry.py,sha256=Ajk3SyMSKDi2g9SJYUtTgg7PZkAgydaHcpbGuEN3S94,311
|
73
|
-
osbot_utils/helpers/Zip_Bytes.py,sha256=
|
74
|
+
osbot_utils/helpers/Zip_Bytes.py,sha256=KB5zWfCf6ET4alNfqNrSp5DxZ3Jp9oDHpc6tK2iO_qg,4320
|
74
75
|
osbot_utils/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
76
|
osbot_utils/helpers/ast/Ast.py,sha256=lcPQOSxXI6zgmMnIVF9WM6ISqViWX-sq4d_UC0CDG8s,1155
|
76
77
|
osbot_utils/helpers/ast/Ast_Base.py,sha256=5rHMupBlN_n6lOC31UnSW_lWqxqxaE31v0gn-t32OgQ,3708
|
@@ -293,20 +294,20 @@ osbot_utils/utils/Json.py,sha256=8fpYFXzNPSrwYfXMt3eGKpe-lhxh1kEaCRae1X1943A,666
|
|
293
294
|
osbot_utils/utils/Json_Cache.py,sha256=mLPkkDZN-3ZVJiDvV1KBJXILtKkTZ4OepzOsDoBPhWg,2006
|
294
295
|
osbot_utils/utils/Lists.py,sha256=tPz5x5s3sRO97WZ_nsxREBPC5cwaHrhgaYBhsrffTT8,5599
|
295
296
|
osbot_utils/utils/Misc.py,sha256=4MkG2BE1VzZfV4KPzYZ4jVAoUwoA3pTTVPi609ldLGA,16961
|
296
|
-
osbot_utils/utils/Objects.py,sha256=
|
297
|
+
osbot_utils/utils/Objects.py,sha256=fqDy8AFimxkUxGAMQBkqYCvQnT_TDcR8EwHOMWPxorc,18992
|
297
298
|
osbot_utils/utils/Png.py,sha256=V1juGp6wkpPigMJ8HcxrPDIP4bSwu51oNkLI8YqP76Y,1172
|
298
299
|
osbot_utils/utils/Process.py,sha256=lr3CTiEkN3EiBx3ZmzYmTKlQoPdkgZBRjPulMxG-zdo,2357
|
299
300
|
osbot_utils/utils/Python_Logger.py,sha256=tx8N6wRKL3RDHboDRKZn8SirSJdSAE9cACyJkxrThZ8,12792
|
300
301
|
osbot_utils/utils/Regex.py,sha256=0ubgp8HKsS3PNe2H6XlzMIcUuV7jhga3VkQVDNOJWuA,866
|
301
302
|
osbot_utils/utils/Status.py,sha256=Yq4s0TelXgn0i2QjCP9V8mP30GabXp_UL-jjM6Iwiw4,4305
|
302
|
-
osbot_utils/utils/Str.py,sha256=
|
303
|
+
osbot_utils/utils/Str.py,sha256=pHcPE3xZ0aBz35aXIW2hdA5WN6vhRqsNT8A-7MNNIY0,3252
|
303
304
|
osbot_utils/utils/Threads.py,sha256=lnh4doZWYUIoWBZRU_780QPeAIKGDh7INuqmU8Fzmdc,3042
|
304
305
|
osbot_utils/utils/Toml.py,sha256=-_Yv5T8ZhGGoDSSoNEdFhSsXiK_JPjGkPijm4JoeHSk,1669
|
305
306
|
osbot_utils/utils/Version.py,sha256=Ww6ChwTxqp1QAcxOnztkTicShlcx6fbNsWX5xausHrg,422
|
306
|
-
osbot_utils/utils/Zip.py,sha256=
|
307
|
+
osbot_utils/utils/Zip.py,sha256=pR6sKliUY0KZXmqNzKY2frfW-YVQEVbLKiyqQX_lc-8,14052
|
307
308
|
osbot_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
308
|
-
osbot_utils/version,sha256=
|
309
|
-
osbot_utils-1.
|
310
|
-
osbot_utils-1.
|
311
|
-
osbot_utils-1.
|
312
|
-
osbot_utils-1.
|
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,,
|
File without changes
|
File without changes
|