osbot-utils 1.27.0__py3-none-any.whl → 1.28.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.
@@ -0,0 +1,79 @@
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.Zip import zip_bytes_empty, zip_bytes__files, zip_bytes__add_file, zip_bytes__add_files, \
5
+ zip_bytes__replace_files, zip_bytes__replace_file, zip_bytes__file_list, zip_bytes__file, \
6
+ zip_bytes__add_file__from_disk, zip_bytes__add_files__from_disk, zip_files, zip_file__files
7
+
8
+
9
+ class Zip_Bytes(Type_Safe):
10
+ zip_bytes : bytes
11
+
12
+ def __enter__(self):
13
+ self.zip_bytes = zip_bytes_empty()
14
+ return self
15
+
16
+ def __exit__(self, exc_type, exc_val, exc_tb):
17
+ pass
18
+
19
+ def add_file(self, file_path, file_contents):
20
+ self.zip_bytes = zip_bytes__add_file(self.zip_bytes, file_path, file_contents)
21
+ return self
22
+
23
+ def add_file__from_disk(self, base_path, file_to_add):
24
+ self.zip_bytes = zip_bytes__add_file__from_disk(self.zip_bytes, base_path, file_to_add)
25
+ return self
26
+
27
+ def add_files(self, files_to_add):
28
+ self.zip_bytes = zip_bytes__add_files(self.zip_bytes, files_to_add)
29
+ return self
30
+
31
+ def add_files__from_disk(self, base_path, files_to_add):
32
+ self.zip_bytes = zip_bytes__add_files__from_disk(self.zip_bytes, base_path, files_to_add)
33
+ return self
34
+
35
+ def add_folder__from_disk(self, base_path, folder_to_add, pattern="*"):
36
+ files_to_add = files_list(folder_to_add, pattern=pattern)
37
+ return self.add_files__from_disk(base_path, files_to_add)
38
+
39
+ def add_from_zip_file(self, path_zip_file):
40
+ files_to_add = zip_file__files(path_zip_file)
41
+ self.add_files(files_to_add)
42
+ return self
43
+
44
+ def empty(self):
45
+ return self.size() == 0
46
+
47
+ def file(self, file_path):
48
+ return zip_bytes__file(self.zip_bytes, file_path)
49
+
50
+ def files(self):
51
+ return zip_bytes__files(self.zip_bytes)
52
+
53
+ def files_list(self):
54
+ return zip_bytes__file_list(self.zip_bytes)
55
+
56
+ def print_files_list(self):
57
+ pprint(self.files_list())
58
+ return self
59
+ def replace_files(self, files_to_replace):
60
+ self.zip_bytes = zip_bytes__replace_files(self.zip_bytes, files_to_replace)
61
+ return self
62
+
63
+ def replace_file(self, file_path, file_contents):
64
+ self.zip_bytes = zip_bytes__replace_file(self.zip_bytes, file_path, file_contents)
65
+ return self
66
+
67
+ def save(self, path=None):
68
+ if path is None:
69
+ path = temp_file(extension='.zip')
70
+ zip_file = file_create_from_bytes(bytes=self.zip_bytes, path=path)
71
+ return zip_file
72
+
73
+ def save_to(self, path):
74
+ parent_folder_create(path) # make sure the parent folder exists
75
+ return self.save(path)
76
+
77
+
78
+ def size(self):
79
+ return len(self.files_list())
@@ -1,7 +1,7 @@
1
1
  from osbot_utils.testing.Temp_Folder import Temp_Folder
2
2
  from osbot_utils.utils.Files import Files, is_folder, file_exists, file_name, file_move_to_folder, file_move, \
3
3
  file_move_to
4
- from osbot_utils.utils.Zip import zip_folder, zip_file_list
4
+ from osbot_utils.utils.Zip import zip_folder, zip_file__list
5
5
 
6
6
 
7
7
  class Temp_Zip():
@@ -35,7 +35,7 @@ class Temp_Zip():
35
35
  return self.zip_file
36
36
 
37
37
  def files(self):
38
- return zip_file_list(self.zip_file)
38
+ return zip_file__list(self.zip_file)
39
39
 
40
40
  def print_path(self):
41
41
  print()
@@ -3,7 +3,7 @@ import io
3
3
  from osbot_utils.testing.Temp_File import Temp_File
4
4
  from osbot_utils.testing.Temp_Folder import Temp_Folder
5
5
  from osbot_utils.utils.Files import is_file, is_folder, files_recursive, filter_parent_folder, temp_file
6
- from osbot_utils.utils.Zip import zip_files_to_bytes, zip_bytes_file_list, zip_bytes_add_file, zip_bytes_get_file
6
+ from osbot_utils.utils.Zip import zip_files_to_bytes, zip_bytes__file_list, zip_bytes__add_file, zip_bytes__get_file
7
7
 
8
8
 
9
9
  class Temp_Zip_In_Memory:
@@ -76,14 +76,14 @@ class Temp_Zip_In_Memory:
76
76
  for items in self.targets_as_content:
77
77
  file_path = items.get('file_path')
78
78
  file_contents = items.get('file_contents')
79
- zip_bytes = zip_bytes_add_file(zip_bytes, file_path, file_contents)
79
+ zip_bytes = zip_bytes__add_file(zip_bytes, file_path, file_contents)
80
80
  return zip_bytes
81
81
 
82
82
  def zip_bytes_file_content(self, file_path):
83
- return zip_bytes_get_file(self.zip_bytes(), file_path)
83
+ return zip_bytes__get_file(self.zip_bytes(), file_path)
84
84
 
85
85
  def zip_bytes_files(self):
86
- return zip_bytes_file_list(self.zip_bytes())
86
+ return zip_bytes__file_list(self.zip_bytes())
87
87
 
88
88
  def zip_buffer(self):
89
89
  targets = self.target_files_with_root_folder()
@@ -81,10 +81,12 @@ class Files:
81
81
  return glob.glob(path_pattern, recursive=recursive)
82
82
 
83
83
  @staticmethod
84
- def files(path, pattern= '*.*'): # todo: check behaviour and improve ability to detect file (vs folders)
84
+ def files(path, pattern= '*.*', only_files=True):
85
85
  result = []
86
86
  for file in Path(path).rglob(pattern):
87
- result.append(str(file)) # todo: see if there is a better way to do this conversion to string
87
+ if only_files and is_not_file(file):
88
+ continue
89
+ result.append(str(file)) # todo: see if there is a better way to do this conversion to string
88
90
  return sorted(result)
89
91
 
90
92
  @staticmethod
@@ -196,7 +198,7 @@ class Files:
196
198
  return path
197
199
 
198
200
  @staticmethod
199
- def folder_create_in_parent(path, name):
201
+ def folder_create_in_parent(path, name): # todo: revise the naming of this method, since it really doesn't have to do with 'parent' (it will depend on value of name)
200
202
  folder_path = path_combine(path, name)
201
203
  return folder_create(folder_path)
202
204
 
@@ -466,6 +468,9 @@ def all_parent_folders(path=None, include_path=False):
466
468
  break
467
469
  return parent_directories
468
470
 
471
+ def is_not_file(target):
472
+ return is_file(target) is False
473
+
469
474
  def file_move(source_file, target_file):
470
475
  if file_exists(source_file):
471
476
  file_copy(source_file, target_file)
@@ -486,6 +491,15 @@ def folders_names_in_folder(target):
486
491
  folders = folders_in_folder(target)
487
492
  return folders_names(folders)
488
493
 
494
+ def parent_folder_create(target):
495
+ return folder_create(parent_folder(target))
496
+
497
+ def parent_folder_exists(target):
498
+ return folder_exists(parent_folder(target))
499
+
500
+ def parent_folder_not_exists(target):
501
+ return parent_folder_exists(target) is False
502
+
489
503
  def stream_to_bytes(stream):
490
504
  return stream.read()
491
505
 
osbot_utils/utils/Zip.py CHANGED
@@ -6,10 +6,13 @@ import tarfile
6
6
  import zipfile
7
7
  from os.path import abspath
8
8
 
9
- from osbot_utils.utils.Files import temp_folder, folder_files, temp_file, is_file, file_copy, file_move
9
+ from osbot_utils.utils.Files import temp_folder, folder_files, temp_file, is_file, file_copy, file_move, file_exists, \
10
+ file_contents_as_bytes
10
11
 
12
+ #########################
13
+ # actions on gz_tar_bytes
11
14
 
12
- def gz_tar_bytes_file_list(gz_bytes):
15
+ def gz_tar_bytes__file_list(gz_bytes):
13
16
  gz_buffer_from_bytes = io.BytesIO(gz_bytes)
14
17
  with gzip.GzipFile(fileobj=gz_buffer_from_bytes, mode='rb') as gz:
15
18
  decompressed_data = gz.read()
@@ -17,7 +20,7 @@ def gz_tar_bytes_file_list(gz_bytes):
17
20
  with tarfile.open(fileobj=tar_buffer_from_bytes, mode='r:') as tar:
18
21
  return sorted(tar.getnames())
19
22
 
20
- def gz_tar_bytes_get_file(gz_bytes, tar_file_path):
23
+ def gz_tar_bytes__get_file(gz_bytes, tar_file_path):
21
24
  gz_buffer_from_bytes = io.BytesIO(gz_bytes)
22
25
  with gzip.GzipFile(fileobj=gz_buffer_from_bytes, mode='rb') as gz:
23
26
  decompressed_data = gz.read()
@@ -29,7 +32,10 @@ def gz_tar_bytes_get_file(gz_bytes, tar_file_path):
29
32
  else:
30
33
  raise FileNotFoundError(f"The file {tar_file_path} was not found in the tar archive.")
31
34
 
32
- def gz_zip_bytes_file_list(gz_bytes):
35
+ #########################
36
+ # actions on gz_zip_bytes
37
+
38
+ def gz_zip_bytes__file_list(gz_bytes):
33
39
  gz_buffer_from_bytes = io.BytesIO(gz_bytes)
34
40
  with gzip.GzipFile(fileobj=gz_buffer_from_bytes, mode='rb') as gz:
35
41
  decompressed_data = gz.read()
@@ -37,28 +43,90 @@ def gz_zip_bytes_file_list(gz_bytes):
37
43
  with zipfile.ZipFile(zip_buffer_from_bytes, 'r') as zf:
38
44
  return sorted(zf.namelist())
39
45
 
40
- def unzip_file(zip_file, target_folder=None, format='zip'):
41
- target_folder = target_folder or temp_folder()
42
- shutil.unpack_archive(zip_file, extract_dir=target_folder, format=format)
43
- return target_folder
46
+ #########################
47
+ # actions on zipped bytes
48
+
49
+ def zip_bytes__add_file(zip_bytes, zip_file_path, file_contents): # todo: see if this is actually a valid use case (or if we should be using replace in all scenarios)
50
+ return zip_bytes__add_files(zip_bytes, {zip_file_path: file_contents})
51
+
52
+ def zip_bytes__add_file__from_disk(zip_bytes, base_path, file_to_add):
53
+ return zip_bytes__add_files__from_disk(zip_bytes, base_path, files_to_add=[file_to_add])
54
+
55
+ def zip_bytes__add_files__from_disk(zip_bytes, base_path, files_to_add, replace_files=True):
56
+ zip_files_to_add = {}
57
+ if base_path[:-1] != '/':
58
+ base_path += "/"
59
+ for file_to_add in files_to_add:
60
+ if file_exists(file_to_add):
61
+ file_contents = file_contents_as_bytes(file_to_add)
62
+ zip_file_path = file_to_add.replace(base_path, '')
63
+ zip_files_to_add[zip_file_path] = file_contents
64
+
65
+ if replace_files:
66
+ return zip_bytes__replace_files(zip_bytes, zip_files_to_add)
67
+ else:
68
+ return zip_bytes__add_files(zip_bytes, zip_files_to_add) # todo: see if this is actually a valid use case (or if we should be using replace in all scenarios)
69
+
70
+ def zip_bytes__add_files(zip_bytes, files_to_add): # todo: see if this is actually a valid use case (or if we should be using replace in all scenarios)
71
+ zip_buffer = io.BytesIO(zip_bytes) # Create a BytesIO buffer from the input zip bytes
44
72
 
45
- def zip_bytes_add_file(zip_bytes, zip_file_path, file_contents):
46
- if type(file_contents) is str:
47
- file_contents = file_contents.encode('utf-8')
48
- elif type(file_contents) is not bytes:
49
- return None
50
- zip_buffer = io.BytesIO(zip_bytes)
51
73
  with zipfile.ZipFile(zip_buffer, 'a', zipfile.ZIP_DEFLATED) as zf:
52
- zf.writestr(zip_file_path, file_contents)
74
+ for file_path, file_contents in files_to_add.items():
75
+ if isinstance(file_contents, str):
76
+ file_contents = file_contents.encode('utf-8')
77
+ elif not isinstance(file_contents, bytes):
78
+ continue
79
+ zf.writestr(file_path, file_contents)
53
80
 
54
81
  return zip_buffer.getvalue()
55
82
 
56
- def zip_bytes_get_file(zip_bytes, zip_file_path):
83
+ def zip_bytes__file(zip_bytes, zip_file_path):
57
84
  zip_buffer = io.BytesIO(zip_bytes)
58
85
  with zipfile.ZipFile(zip_buffer, 'r') as zf:
59
- return zf.read(zip_file_path)
86
+ if zip_file_path in zf.namelist():
87
+ return zf.read(zip_file_path)
88
+
89
+ def zip_bytes__files(zip_bytes):
90
+ zip_buffer = io.BytesIO(zip_bytes) # Create a BytesIO buffer from the input zip bytes
91
+ files_dict = {} # Create a dictionary to store file contents
92
+
93
+ with zipfile.ZipFile(zip_buffer, 'r') as zf: # Open the zip file in read mode
94
+ for file_name in zf.namelist(): # Iterate over each file in the zip archive
95
+ files_dict[file_name] = zf.read(file_name) # Read the content of the file
96
+
97
+ return files_dict # Return the dictionary with file contents
98
+
99
+ def zip_bytes__file_list(zip_bytes):
100
+ zip_buffer_from_bytes = io.BytesIO(zip_bytes)
101
+ with zipfile.ZipFile(zip_buffer_from_bytes, 'r') as zf:
102
+ return sorted(zf.namelist())
103
+
104
+ def zip_bytes__remove_file(zip_bytes, file_to_remove):
105
+ return zip_bytes__remove_files(zip_bytes, [file_to_remove])
106
+
107
+ def zip_bytes__remove_files(zip_bytes, files_to_remove):
108
+ files_to_remove = set(files_to_remove) # Convert files_to_remove to a set for faster lookup
109
+ zip_buffer = io.BytesIO(zip_bytes) # Create a BytesIO buffer from the input zip bytes
110
+ new_zip_buffer = io.BytesIO() # Create a new BytesIO buffer for the updated zip
111
+
112
+ with zipfile.ZipFile(zip_buffer, 'r') as original_zip:
113
+ with zipfile.ZipFile(new_zip_buffer, 'w') as new_zip:
114
+ for item in original_zip.infolist(): # Iterate over each item in the original zip file
115
+ if item.filename not in files_to_remove: # Read the original content and write it to the new zip file
116
+ new_zip.writestr(item, original_zip.read(item.filename))
117
+ return new_zip_buffer.getvalue() # Get the updated zip content as bytes
118
+
119
+ def zip_bytes__replace_file(zip_bytes, zip_file_path, file_contents):
120
+ files_to_replace = {zip_file_path: file_contents }
121
+ return zip_bytes__replace_files(zip_bytes, files_to_replace)
122
+
123
+ def zip_bytes__replace_files(zip_bytes, files_to_replace):
124
+ zip_bytes__without_files = zip_bytes__remove_files(zip_bytes , set(files_to_replace))
125
+ zip_bytes__with_replaced_files = zip_bytes__add_files (zip_bytes__without_files, files_to_replace )
126
+ return zip_bytes__with_replaced_files
127
+
60
128
 
61
- def zip_bytes_extract_to_folder(zip_bytes, target_folder=None):
129
+ def zip_bytes__unzip(zip_bytes, target_folder=None):
62
130
  target_folder = target_folder or temp_folder() # Use the provided target folder or create a temporary one
63
131
  zip_buffer = io.BytesIO(zip_bytes) # Create a BytesIO buffer from the zip bytes
64
132
  with zipfile.ZipFile(zip_buffer, 'r') as zf: # Open the zip file from the buffer
@@ -66,10 +134,33 @@ def zip_bytes_extract_to_folder(zip_bytes, target_folder=None):
66
134
  return target_folder # Return the path of the target folder
67
135
 
68
136
 
69
- def zip_bytes_file_list(zip_bytes):
70
- zip_buffer_from_bytes = io.BytesIO(zip_bytes)
71
- with zipfile.ZipFile(zip_buffer_from_bytes, 'r') as zf:
72
- return sorted(zf.namelist())
137
+ ########################
138
+ # actions on zipped file
139
+
140
+ def zip_file__list(path_zip_file):
141
+ if is_file(path_zip_file):
142
+ with zipfile.ZipFile(path_zip_file) as zip_file:
143
+ return sorted(zip_file.namelist())
144
+ return []
145
+
146
+ def zip_file__files(path_zip_file):
147
+ if is_file(path_zip_file):
148
+ zip_bytes = file_contents_as_bytes(path_zip_file)
149
+ return zip_bytes__files(zip_bytes)
150
+ return []
151
+
152
+ def zip_file__unzip(path_zip_file, target_folder=None, format='zip'):
153
+ target_folder = target_folder or temp_folder()
154
+ shutil.unpack_archive(path_zip_file, extract_dir=target_folder, format=format)
155
+ return target_folder
156
+
157
+ # zip creation actions
158
+ def zip_bytes_empty():
159
+
160
+ zip_buffer = io.BytesIO() # Create a BytesIO buffer to hold the zip file
161
+ with zipfile.ZipFile(zip_buffer, mode='w') as _: # Use the zipfile.ZipFile class to create an empty zip file
162
+ pass # No files to add, so we just create the zip structure
163
+ return zip_buffer.getvalue() # Get the zip file content as bytes
73
164
 
74
165
  def zip_bytes_to_file(zip_bytes, target_file=None):
75
166
  if target_file is None:
@@ -103,7 +194,6 @@ def zip_folder_to_file (root_dir, target_file):
103
194
  zip_file = zip_folder(root_dir)
104
195
  return file_move(zip_file, target_file)
105
196
 
106
-
107
197
  def zip_folder_to_bytes(root_dir): # todo add unit test
108
198
  zip_buffer = io.BytesIO() # Create a BytesIO buffer to hold the zipped file
109
199
  with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: # Create a ZipFile object with the buffer as the target
@@ -115,12 +205,6 @@ def zip_folder_to_bytes(root_dir): # todo add unit test
115
205
  zip_buffer.seek(0) # Reset buffer position
116
206
  return zip_buffer
117
207
 
118
- def zip_file_list(path):
119
- if is_file(path):
120
- with zipfile.ZipFile(path) as zip_file:
121
- return sorted(zip_file.namelist())
122
- return []
123
-
124
208
  def zip_files(base_folder, file_pattern="*.*", target_file=None):
125
209
  base_folder = abspath(base_folder)
126
210
  file_list = folder_files(base_folder, file_pattern)
@@ -134,8 +218,18 @@ def zip_files(base_folder, file_pattern="*.*", target_file=None):
134
218
 
135
219
  return target_file
136
220
 
137
-
221
+ ###########################
138
222
  # extra function's mappings
139
- file_unzip = unzip_file
140
- folder_zip = zip_folder
141
- zip_bytes_unzip_to_folder = zip_bytes_extract_to_folder
223
+
224
+ file_unzip = zip_file__unzip
225
+ folder_zip = zip_folder
226
+
227
+ unzip_file = zip_file__unzip
228
+
229
+ zip_bytes__extract_to_folder = zip_bytes__unzip
230
+ zip_bytes__file_contents = zip_bytes__file
231
+ zip_bytes__get_file = zip_bytes__file
232
+ zip_bytes__unzip_to_folder = zip_bytes__unzip
233
+
234
+ zip_list_files = zip_file__list
235
+ zip_file__file_list = zip_file__list
osbot_utils/version CHANGED
@@ -1 +1 @@
1
- v1.27.0
1
+ v1.28.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: osbot_utils
3
- Version: 1.27.0
3
+ Version: 1.28.0
4
4
  Summary: OWASP Security Bot - Utils
5
5
  Home-page: https://github.com/owasp-sbot/OSBot-Utils
6
6
  License: MIT
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
22
22
 
23
23
  Powerful Python util methods and classes that simplify common apis and tasks.
24
24
 
25
- ![Current Release](https://img.shields.io/badge/release-v1.27.0-blue)
25
+ ![Current Release](https://img.shields.io/badge/release-v1.28.0-blue)
26
26
  [![codecov](https://codecov.io/gh/owasp-sbot/OSBot-Utils/graph/badge.svg?token=GNVW0COX1N)](https://codecov.io/gh/owasp-sbot/OSBot-Utils)
27
27
 
28
28
 
@@ -63,6 +63,7 @@ osbot_utils/helpers/Print_Table.py,sha256=LEXbyqGg_6WSraI4cob4bNNSu18ddqvALp1zGK
63
63
  osbot_utils/helpers/Python_Audit.py,sha256=shpZlluJwqJBAlad6xN01FkgC1TsQ48RLvR5ZjmrKa4,1539
64
64
  osbot_utils/helpers/Random_Seed.py,sha256=14btja8LDN9cMGWaz4fCNcMRU_eyx49gas-_PQvHgy4,634
65
65
  osbot_utils/helpers/Type_Registry.py,sha256=Ajk3SyMSKDi2g9SJYUtTgg7PZkAgydaHcpbGuEN3S94,311
66
+ osbot_utils/helpers/Zip_Bytes.py,sha256=Ls_AW_-vCusZxL3-Jfl9bJfOpwl4e7nTzxrXSWJ6Ty8,2850
66
67
  osbot_utils/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
68
  osbot_utils/helpers/ast/Ast.py,sha256=lcPQOSxXI6zgmMnIVF9WM6ISqViWX-sq4d_UC0CDG8s,1155
68
69
  osbot_utils/helpers/ast/Ast_Base.py,sha256=5rHMupBlN_n6lOC31UnSW_lWqxqxaE31v0gn-t32OgQ,3708
@@ -254,8 +255,8 @@ osbot_utils/testing/Temp_File.py,sha256=yZBL9MmcNU4PCQ4xlF4rSss4GylKoX3T_AJF-BlQ
254
255
  osbot_utils/testing/Temp_Folder.py,sha256=gewV0L3B4ScKyorkVOQaQ-vz4DbeVRnqxCMS9A0eVLo,4953
255
256
  osbot_utils/testing/Temp_Sys_Path.py,sha256=gOMD-7dQYQlejoDYUqsrmuZQ9DLC07ymPZB3zYuNmG4,256
256
257
  osbot_utils/testing/Temp_Web_Server.py,sha256=0A-gZsd0_3wRj2YuBEOWyV2rhT6dcS2BlArngPXGTtk,3186
257
- osbot_utils/testing/Temp_Zip.py,sha256=Qaxu7J4zpdYn3bXgYOLT5BYRWu8-QgkUBA-LtqpNoS0,1487
258
- osbot_utils/testing/Temp_Zip_In_Memory.py,sha256=LhZZR3jQtRywdQNC71hvFk1I3Yek4eMPXpNTT8S6zHo,3278
258
+ osbot_utils/testing/Temp_Zip.py,sha256=gppbJchk4tw_bu-7Vt6iJS9mGxeCvNNMMDzeVKHqRv8,1489
259
+ osbot_utils/testing/Temp_Zip_In_Memory.py,sha256=ibDIWt3K4CX558PbkLbX3InHyWYZcwQwajFm1kAPW5U,3284
259
260
  osbot_utils/testing/Unit_Test.py,sha256=MReR_wDGbbXFDPz7cmuGflcTxRB6TBnO9mYqRpSq8Pk,1304
260
261
  osbot_utils/testing/Unzip_File.py,sha256=V5H97XO9rlvG5EYOSzAH4kTtAH1ohZ8R8ImvJh46ZNg,1177
261
262
  osbot_utils/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -265,7 +266,7 @@ osbot_utils/utils/Csv.py,sha256=oHLVpjRJqrLMz9lubMCNEoThXWju5rNTprcwHc1zq2c,1012
265
266
  osbot_utils/utils/Dev.py,sha256=HibpQutYy_iG8gGV8g1GztxNN4l29E4Bi7UZaVL6-L8,1203
266
267
  osbot_utils/utils/Env.py,sha256=uYLhqVXqqgfh03Zmf9Vdy9zFdFf0rasW6lteh_VM1xQ,5078
267
268
  osbot_utils/utils/Exceptions.py,sha256=KyOUHkXQ_6jDTq04Xm261dbEZuRidtsM4dgzNwSG8-8,389
268
- osbot_utils/utils/Files.py,sha256=fWASqyfhUttKw4g3AfbhzplUPZL228nBzBSVqgLylvQ,20374
269
+ osbot_utils/utils/Files.py,sha256=SLuu_RuugTKvXi0zOJh_mkr2FE6TUyQvW60vgZbkHEY,20836
269
270
  osbot_utils/utils/Functions.py,sha256=0E6alPJ0fJpBiJgFOWooCOi265wSRyxxXAJ5CELBnso,3498
270
271
  osbot_utils/utils/Http.py,sha256=Z8V149M2HDrKBoXkDD5EXgqTGx6vQoUqXugXK__wcuw,4572
271
272
  osbot_utils/utils/Int.py,sha256=PmlUdU4lSwf4gJdmTVdqclulkEp7KPCVUDO6AcISMF4,116
@@ -281,10 +282,10 @@ osbot_utils/utils/Status.py,sha256=Yq4s0TelXgn0i2QjCP9V8mP30GabXp_UL-jjM6Iwiw4,4
281
282
  osbot_utils/utils/Str.py,sha256=kxdY8ROX4FdJtCaMTfOc8fK_xcDICprNkefHu2MMNU4,2585
282
283
  osbot_utils/utils/Toml.py,sha256=dqiegndCJF7V1YT1Tc-b0-Bl6QWyL5q30urmQwMXfMQ,1402
283
284
  osbot_utils/utils/Version.py,sha256=Ww6ChwTxqp1QAcxOnztkTicShlcx6fbNsWX5xausHrg,422
284
- osbot_utils/utils/Zip.py,sha256=yGTRuE1gaUMZ8T_lnM0PAU31Kwd-TrzEuuWysJVgHGA,6868
285
+ osbot_utils/utils/Zip.py,sha256=nj3_PrvKRlyeSWcFlbvEwhmu87Gl-siUfIoXxDPE32c,12056
285
286
  osbot_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
286
- osbot_utils/version,sha256=geP7KEJu4xooR8XnusEbz-qnkD4i7bGqu2XFp8jXSIo,8
287
- osbot_utils-1.27.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
288
- osbot_utils-1.27.0.dist-info/METADATA,sha256=DGQYf9EpRkKvgPfZZ2Z2xQVy8sLD6ODZwAUZfvr68nU,1266
289
- osbot_utils-1.27.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
290
- osbot_utils-1.27.0.dist-info/RECORD,,
287
+ osbot_utils/version,sha256=MNi3rJWAauptL9RTT15nAiO62YEIThbbzD7BICVSPoQ,8
288
+ osbot_utils-1.28.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
289
+ osbot_utils-1.28.0.dist-info/METADATA,sha256=LvLJ-gezDkfRszvTQeU-WyLaVX-z0RJmohGZ1qdQkk8,1266
290
+ osbot_utils-1.28.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
291
+ osbot_utils-1.28.0.dist-info/RECORD,,