atomicshop 2.18.5__py3-none-any.whl → 2.18.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 atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.18.5'
4
+ __version__ = '2.18.7'
@@ -107,18 +107,27 @@ def _search_in_archive(
107
107
 
108
108
  # Iterate over each file in the archive.
109
109
  for item_index, item in enumerate(file_info_list):
110
- if item.filename.endswith('/'): # Skip directories
111
- continue
112
-
113
110
  # At this stage we will get the bytes of the archived file, which is an 'item' in the archive.
114
111
  archived_file_bytes = None
115
112
  # If the main archive is zip we will use the 'open' method, if it's 7z we will use the 'read' method.
116
113
  if archive_type == 'zip':
114
+ # Skip directories.
115
+ if item.filename.endswith('/'):
116
+ continue
117
+
117
118
  with arch_obj.open(item) as file_data:
118
119
  archived_file_bytes = file_data.read()
119
120
  elif archive_type == '7z':
121
+ # Skip directories.
122
+ if item.is_directory:
123
+ continue
124
+
125
+ # If 'SevenZipFile.red()' is used once, the second time you need to read it you will need to reset the
126
+ # SevenZipFile object in order to read again:
127
+ # https://py7zr.readthedocs.io/en/latest/api.html#py7zr.SevenZipFile.read
120
128
  file_dict = arch_obj.read([item.filename])
121
129
  archived_file_bytes = file_dict[item.filename].read()
130
+ arch_obj.reset()
122
131
 
123
132
  # After we get the file bytes we will check if the file matches the callback functions.
124
133
  callback_matched = False
@@ -129,7 +138,7 @@ def _search_in_archive(
129
138
  if callback_matched:
130
139
  _handle_file_extraction(item, extract_file_to_path, archived_file_bytes)
131
140
  else:
132
- if recursive and (zips.is_zip_zipfile(archived_file_bytes) or sevenzs.is_7z(archived_file_bytes)):
141
+ if recursive and (zips.is_zip_zipfile(archived_file_bytes) or sevenzs.is_7z_magic_number(archived_file_bytes)):
133
142
  _search_archive_content(
134
143
  archived_file_bytes, file_names, results, found_set, case_sensitive, return_first_only,
135
144
  recursive, callback_functions, extract_file_to_path)
@@ -187,7 +196,7 @@ def _get_archive_type(file_object) -> Union[str, None]:
187
196
 
188
197
  if zips.is_zip_zipfile(file_object):
189
198
  return 'zip'
190
- elif sevenzs.is_7z(file_object):
199
+ elif sevenzs.is_7z_magic_number(file_object):
191
200
  return '7z'
192
201
  else:
193
202
  raise UnknownArchiveType(f"{file_object[:10]} is not a known archive type.")
@@ -4,8 +4,50 @@ from typing import Union
4
4
  import py7zr
5
5
 
6
6
 
7
- def is_7z(file_object: Union[str, bytes]) -> bool:
7
+ def is_7z_magic_number(
8
+ file_object: Union[str, bytes]
9
+ ) -> bool:
8
10
  """
11
+ Function checks if the file is a 7z file, by checking the magic number.
12
+
13
+ :param file_object: can be two types:
14
+ string, full path to the file.
15
+ bytes or BytesIO, the bytes of the file.
16
+ :return: boolean.
17
+ """
18
+
19
+ if isinstance(file_object, str):
20
+ with open(file_object, 'rb') as file:
21
+ data = file.read(6)
22
+ elif isinstance(file_object, bytes):
23
+ data = file_object
24
+ else:
25
+ raise TypeError(f'The file_object must be a string or bytes, not {type(file_object)}')
26
+
27
+ # Check if the data is at least 6 bytes long
28
+ if len(data) < 6:
29
+ return False
30
+
31
+ # 7z file signature (magic number)
32
+ # The signature is '7z' followed by 'BCAF271C'
33
+ seven_z_signature = b'7z\xBC\xAF\x27\x1C'
34
+
35
+ # Compare the first 6 bytes of the data with the 7z signature
36
+ result = data.startswith(seven_z_signature)
37
+
38
+ return result
39
+
40
+
41
+ def _is_7z(file_object: Union[str, bytes]) -> bool:
42
+ """
43
+ THIS IS ONLY FOR THE REFERENCE.
44
+
45
+ Used to use this function since it raised 'py7zr.Bad7zFile' if the file was not a 7z file.
46
+ The problem that 'SevenZipFile.testzip()' checks archived files CRCs and returns the first bad file:
47
+ https://py7zr.readthedocs.io/en/latest/api.html#py7zr.SevenZipFile.testzip
48
+ The problem is when the file IS 7z file, but there can be other problems with the file, it can raise a RuntimeError.
49
+ So, better use the 'is_7z_magic_number' function.
50
+
9
51
  Function checks if the file is a 7z file.
10
52
  :param file_object: can be two types:
11
53
  string, full path to the file.
@@ -29,16 +71,3 @@ def is_7z(file_object: Union[str, bytes]) -> bool:
29
71
  except OSError as e:
30
72
  if e.args[0] == 22:
31
73
  return False
32
-
33
-
34
- def _is_7z_magic_number(data):
35
- # Check if the data is at least 6 bytes long
36
- if len(data) < 6:
37
- return False
38
-
39
- # 7z file signature (magic number)
40
- # The signature is '7z' followed by 'BCAF271C'
41
- seven_z_signature = b'7z\xBC\xAF\x27\x1C'
42
-
43
- # Compare the first 6 bytes of the data with the 7z signature
44
- return data.startswith(seven_z_signature)
atomicshop/filesystem.py CHANGED
@@ -1280,70 +1280,20 @@ def create_dict_of_paths_list(list_of_paths: list) -> list:
1280
1280
 
1281
1281
  def create_dict_of_path(
1282
1282
  path: str,
1283
- # structure_dict: dict,
1284
1283
  structure_list: list,
1285
- add_data_to_entry: any = None,
1286
- add_data_key: str = 'addon',
1287
- parent_entry: str = None
1284
+ add_data_to_entry: list[dict[str, any]] = None
1288
1285
  ):
1289
1286
  """
1290
1287
  The function receives a path and a list, and adds the path to the list.
1291
-
1292
1288
  Check the working example from 'create_dict_of_paths_list' function.
1293
1289
 
1294
1290
  :param path: string, path.
1295
1291
  :param structure_list: list to add the path to.
1296
- :param add_data_to_entry: any, data to add to the entry.
1297
- :param add_data_key: string, key to add the data to.
1298
- :param parent_entry: string, for internal use to pass the current parent entry.
1292
+ :param add_data_to_entry: a list of dicts with data to add to the entry.
1293
+ dict format: {key: data}
1299
1294
  :return:
1300
1295
  """
1301
1296
 
1302
- # # Normalize path for cross-platform compatibility
1303
- # normalized_path = path.replace("\\", "/")
1304
- # parts = normalized_path.strip("/").split("/")
1305
- # current_level = structure_dict
1306
- #
1307
- # for part in parts[:-1]: # Iterate through the directories
1308
- # # If the part is not already a key in the current level of the structure, add it
1309
- # if part not in current_level:
1310
- # current_level[part] = {}
1311
- # current_level = current_level[part]
1312
- #
1313
- # # Create the entry for the file with additional data
1314
- # file_entry = {"entry": parts[-1], add_data_key: add_data_to_entry}
1315
- #
1316
- # # We're adding file entries under numeric keys.
1317
- # if isinstance(current_level, dict) and all(isinstance(key, int) for key in current_level.keys()):
1318
- # current_level[len(current_level)] = file_entry
1319
- # else:
1320
- # # Handle cases where there's a mix of numeric keys and directory names
1321
- # # Find the next available numeric key
1322
- # next_key = max([key if isinstance(key, int) else -1 for key in current_level.keys()], default=-1) + 1
1323
- # current_level[next_key] = file_entry
1324
-
1325
- # entries_key_name = "__entries__"
1326
- #
1327
- # # Normalize path for cross-platform compatibility
1328
- # normalized_path = path.replace("\\", "/")
1329
- # parts = normalized_path.strip("/").split("/")
1330
- # current_level = structure_dict
1331
- #
1332
- # for part in parts[:-1]: # Navigate through or create directory structure
1333
- # if part not in current_level:
1334
- # current_level[part] = {}
1335
- # current_level = current_level[part]
1336
- #
1337
- # # Create the entry for the file with additional data
1338
- # file_entry = {"entry": parts[-1], add_data_key: add_data_to_entry}
1339
- #
1340
- # # If the current level (final directory) does not have an "entries" key for files, create it
1341
- # if entries_key_name not in current_level:
1342
- # current_level[entries_key_name] = []
1343
- #
1344
- # # Append the file entry to the list associated with the "entries" key
1345
- # current_level[entries_key_name].append(file_entry)
1346
-
1347
1297
  # Normalize path for cross-platform compatibility
1348
1298
  normalized_path = path.replace("\\", "/")
1349
1299
  parts = normalized_path.strip("/").split("/")
@@ -1351,29 +1301,36 @@ def create_dict_of_path(
1351
1301
  current_level = structure_list
1352
1302
 
1353
1303
  for i, part in enumerate(parts):
1354
- # Determine if this is the last part (a file)
1304
+ # Determine if this is the last part (a file or final component of the path)
1355
1305
  is_last_part = (i == len(parts) - 1)
1356
1306
 
1357
1307
  # Try to find an existing entry for this part
1358
- # noinspection PyTypeChecker
1359
1308
  existing_entry = next((item for item in current_level if item["entry"] == part), None)
1360
1309
 
1361
1310
  if existing_entry is None:
1362
- # For the last part, add the additional data; for directories, just create the structure
1363
- if is_last_part:
1364
- new_entry = {"entry": part, add_data_key: add_data_to_entry, "included": []}
1365
- else:
1366
- new_entry = {"entry": part, "included": []}
1311
+ # Create a new entry
1312
+ new_entry = {"entry": part, "included": []}
1313
+
1314
+ # Add additional data if it's the last part
1315
+ if is_last_part and add_data_to_entry:
1316
+ for data_dict in add_data_to_entry:
1317
+ new_entry.update(data_dict)
1367
1318
 
1368
1319
  current_level.append(new_entry)
1320
+
1369
1321
  # Only update current_level if it's not the last part
1370
1322
  if not is_last_part:
1371
1323
  current_level = new_entry["included"]
1372
1324
  else:
1373
- # If it's not the last part and the entry exists, navigate deeper
1325
+ # If the entry exists and it's not the last part, navigate deeper
1374
1326
  if not is_last_part:
1375
1327
  current_level = existing_entry["included"]
1376
1328
 
1329
+ # If the entry exists and it's the last part, update with additional data
1330
+ if is_last_part and add_data_to_entry:
1331
+ for data_dict in add_data_to_entry:
1332
+ existing_entry.update(data_dict)
1333
+
1377
1334
 
1378
1335
  def list_open_files_in_directory(directory):
1379
1336
  """
@@ -2,7 +2,12 @@ import ipaddress
2
2
  from typing import Union, Literal
3
3
 
4
4
 
5
- def is_ip_address(string_value: str, ip_type: Union[Literal['ipv4', 'ipv6'], None] = None) -> bool:
5
+ def is_ip_address(
6
+ string_value: str,
7
+ ip_type: Union[
8
+ Literal['ipv4', 'ipv6'],
9
+ None] = None
10
+ ) -> bool:
6
11
  """
7
12
  The function checks if the string is an IPv4 or IPv6 address.
8
13
 
@@ -99,3 +99,52 @@ def expand_user_path(user_name, path):
99
99
  pwnam = pwd.getpwnam(user_name)
100
100
  home_dir = pwnam.pw_dir
101
101
  return path.replace("~", home_dir)
102
+
103
+
104
+ def set_folder_permissions(
105
+ folder_path: str,
106
+ username: str = None,
107
+ logged_in_non_sudo_user: bool = False
108
+ ):
109
+ """
110
+ Set ownership and permissions for an existing folder.
111
+
112
+ :param folder_path: Path to the folder (must already exist)
113
+ :param username: Username to assign ownership to (ignored if non_sudo_user=True)
114
+ :param logged_in_non_sudo_user: If True, use the current logged-in user unless running under sudo
115
+ """
116
+
117
+ if not username and not logged_in_non_sudo_user:
118
+ raise ValueError("A username must be provided, or 'non_sudo_user' must be set to True.")
119
+
120
+ # Handle non_sudo_user case
121
+ if logged_in_non_sudo_user:
122
+ # Get the current logged-in user
123
+ username = os.getlogin()
124
+
125
+ # Get the UID and GID of the specified user
126
+ user_info = pwd.getpwnam(username)
127
+ user_uid = user_info.pw_uid
128
+ user_gid = user_info.pw_gid
129
+
130
+ # Change ownership of the folder to the specified user
131
+ # print(f"Changing ownership of {folder_path} to user '{username}'...")
132
+ os.chown(folder_path, user_uid, user_gid)
133
+
134
+ # Set appropriate permissions (read, write, execute for the owner)
135
+ # print(f"Setting permissions for {folder_path}...")
136
+ os.chmod(folder_path, 0o755) # Owner rwx, group r-x, others r-x
137
+
138
+ # print(f"Ownership and permissions updated for folder: '{folder_path}'")
139
+
140
+
141
+ def is_directory_owner(directory_path: str, username: str) -> bool:
142
+ """
143
+ Function checks if the directory is owned by the specified user.
144
+ :param directory_path: str, path to the directory.
145
+ :param username: str, username of the user.
146
+ :return: bool, True / False.
147
+ """
148
+
149
+ uid = pwd.getpwnam(username).pw_uid
150
+ return os.stat(directory_path).st_uid == uid
atomicshop/process.py CHANGED
@@ -82,7 +82,18 @@ def execute_with_live_output(
82
82
  :return: Boolean, If execution was successful, return True, if not - False.
83
83
  """
84
84
 
85
- cmd = _execution_parameters_processing(cmd, wsl)
85
+ if isinstance(cmd, str):
86
+ shell = True
87
+ elif isinstance(cmd, list):
88
+ shell = False
89
+ else:
90
+ raise TypeError(f'cmd must be a string or list, not {type(cmd)}')
91
+
92
+ if wsl:
93
+ if isinstance(cmd, str):
94
+ cmd = 'wsl ' + cmd
95
+ elif isinstance(cmd, list):
96
+ cmd = ['wsl'] + cmd
86
97
 
87
98
  # Needed imports:
88
99
  # from subprocess import Popen, PIPE, STDOUT
@@ -103,7 +114,7 @@ def execute_with_live_output(
103
114
  # The buffer size is system-dependent and usually chosen by the underlying implementation to optimize performance.
104
115
  # # bufsize=0: This means no buffering.
105
116
  # The I/O is unbuffered, and data is written or read from the stream immediately.
106
- with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True) as process:
117
+ with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True, shell=shell) as process:
107
118
  # We'll count the number of lines from 'process.stdout'.
108
119
  counter: int = 0
109
120
  # And also get list of all the lines.
@@ -698,6 +698,10 @@ def find(
698
698
 
699
699
  Example for searching for a value that starts with 'test':
700
700
  filter_query = {'field_name': {'$regex': '^test'}}
701
+
702
+ If you need to escape the string for regex special characters you will typically use:
703
+ re.escape(test)
704
+ If you're string contains characters like parentheses "()", you will need to escape them.
701
705
  $options: The options for the regex search.
702
706
  'i': case-insensitive search.
703
707
  Example for case-insensitive search:
@@ -25,13 +25,21 @@ def add_reusable_address_option(socket_instance):
25
25
  socket_instance.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
26
26
 
27
27
 
28
- def create_ssl_context_for_server():
28
+ def create_ssl_context_for_server() -> ssl.SSLContext:
29
29
  # Creating context with SSL certificate and the private key before the socket
30
30
  # https://docs.python.org/3/library/ssl.html
31
31
  # Creating context for SSL wrapper, specifying "PROTOCOL_TLS_SERVER" will pick the best TLS version protocol for
32
32
  # the server.
33
- return ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
33
+
34
+ ssl_context: ssl.SSLContext = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
35
+
36
+ # # Enforce the use of TLS 1.2 only (disable TLS 1.0, TLS 1.1, and TLS 1.3)
37
+ # ssl_context.options |= ssl.OP_NO_TLSv1 # Disable TLS 1.0
38
+ # ssl_context.options |= ssl.OP_NO_TLSv1_1 # Disable TLS 1.1
39
+ # ssl_context.options |= ssl.OP_NO_TLSv1_3 # Disable TLS 1.3
40
+
34
41
  # return ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
42
+ return ssl_context
35
43
 
36
44
 
37
45
  def create_ssl_context_for_client(
@@ -130,9 +138,9 @@ def load_certificate_and_key_into_server_ssl_context(
130
138
  print_api(message, error_type=True, logger_method="critical", **print_kwargs)
131
139
 
132
140
 
133
- def create_server_ssl_context___load_certificate_and_key(certificate_file_path: str, key_file_path):
141
+ def create_server_ssl_context___load_certificate_and_key(certificate_file_path: str, key_file_path) -> ssl.SSLContext:
134
142
  # Create and set ssl context for server.
135
- ssl_context = create_ssl_context_for_server()
143
+ ssl_context: ssl.SSLContext = create_ssl_context_for_server()
136
144
  # Load certificate into context.
137
145
  load_certificate_and_key_into_server_ssl_context(ssl_context, certificate_file_path, key_file_path)
138
146
  # Return ssl context only.
@@ -82,7 +82,7 @@ class SNISetup:
82
82
  ):
83
83
 
84
84
  # Create SSL Socket to wrap the raw socket with.
85
- ssl_context = creator.create_ssl_context_for_server()
85
+ ssl_context: ssl.SSLContext = creator.create_ssl_context_for_server()
86
86
 
87
87
  self.certificator_instance = certificator.Certificator(
88
88
  ca_certificate_name=self.ca_certificate_name,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.18.5
3
+ Version: 2.18.7
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -38,6 +38,8 @@ Requires-Dist: cryptography
38
38
  Requires-Dist: dnslib
39
39
  Requires-Dist: dnspython
40
40
  Requires-Dist: docker
41
+ Requires-Dist: google-api-python-client
42
+ Requires-Dist: google-generativeai
41
43
  Requires-Dist: numpy
42
44
  Requires-Dist: olefile
43
45
  Requires-Dist: openpyxl
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=djTLkiQczxSrBfseOQFp0kfiVNR7-UsGEJSqlifxWfA,123
1
+ atomicshop/__init__.py,sha256=jd26BqEZL3LhjWeES224RKWQeZ2nlDuEDvDkj1P9FCg,123
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -14,19 +14,19 @@ atomicshop/dns.py,sha256=5Gimq_WY2arqg7BeGmR7P--fGfnH0Dsh8lrOt_H0jRY,6817
14
14
  atomicshop/domains.py,sha256=Rxu6JhhMqFZRcoFs69IoEd1PtYca0lMCG6F1AomP7z4,3197
15
15
  atomicshop/emails.py,sha256=I0KyODQpIMEsNRi9YWSOL8EUPBiWyon3HRdIuSj3AEU,1410
16
16
  atomicshop/file_types.py,sha256=-0jzQMRlmU1AP9DARjk-HJm1tVE22E6ngP2mRblyEjY,763
17
- atomicshop/filesystem.py,sha256=C5GaGmpcGTh11Bd2plZxJmJQVMOZNcZf35mqel2PUxo,60314
17
+ atomicshop/filesystem.py,sha256=8Y5xLaTutQtEjWPxIwH_H88laN7plEMp2r4OLHk-3xc,58259
18
18
  atomicshop/functions.py,sha256=pK8hoCE9z61PtWCxQJsda7YAphrLH1wxU5x-1QJP-sY,499
19
19
  atomicshop/get_process_list.py,sha256=8cxb7gKe9sl4R6H2yMi8J6oe-RkonTvCdKjRFqi-Fs4,6075
20
20
  atomicshop/get_process_name_cmd_dll.py,sha256=CtaSp3mgxxJKCCVW8BLx6BJNx4giCklU_T7USiCEwfc,5162
21
21
  atomicshop/hashing.py,sha256=Le8qGFyt3_wX-zGTeQShz7L2HL_b6nVv9PnawjglyHo,3474
22
22
  atomicshop/http_parse.py,sha256=1Tna9YbOM0rE3t6i_M-klBlwd1KNSA9skA_BqKGXDFc,11861
23
23
  atomicshop/inspect_wrapper.py,sha256=sGRVQhrJovNygHTydqJj0hxES-aB2Eg9KbIk3G31apw,11429
24
- atomicshop/ip_addresses.py,sha256=Hvi4TumEFoTEpKWaq5WNF-YzcRzt24IxmNgv-Mgax1s,1190
24
+ atomicshop/ip_addresses.py,sha256=penRFeJ1-LDVTko4Q0EwK4JiN5cU-KzCBR2VXg9qbUY,1238
25
25
  atomicshop/keyboard_press.py,sha256=1W5kRtOB75fulVx-uF2yarBhW0_IzdI1k73AnvXstk0,452
26
26
  atomicshop/on_exit.py,sha256=Rpg2SaF0aginuO7JYwA49YJYnS8F6K2jUqhjH65WzuU,6889
27
27
  atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erXXdjwtS4,4574
28
28
  atomicshop/print_api.py,sha256=q9dAQCASk3pHp_PtYIpr6iZmRcW_lvrV_gPPNwTMRsw,11152
29
- atomicshop/process.py,sha256=PeLvyixXaCfftdUF3oMbohI1L4MdLtvQVDx2V1Tz_Rk,16662
29
+ atomicshop/process.py,sha256=dmje2YIDPVM8zS38ylAqyOhDBXk6ay_N1xeewKdEIX4,16966
30
30
  atomicshop/python_file_patcher.py,sha256=-uhbUX-um5k-If_XXuOfCr8wMzZ3QE6h9N8xGWw6W_o,5486
31
31
  atomicshop/python_functions.py,sha256=BPZ3sv5DgQs6Xrl3nIMdPABRpgrau3XSrsnDIz-LEwY,6175
32
32
  atomicshop/question_answer_engine.py,sha256=7nM6kGDSFjQNi87b87-kP9lYM0vTjBHn1rEQGNAfdGA,825
@@ -77,9 +77,9 @@ atomicshop/addons/process_list/compiled/Win10x64/process_list.exp,sha256=cbvukIT
77
77
  atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=T2Ncs0MwKlAaCq8UKFMvfQAfcJdDx-nWiHVBfglrLIU,2112
78
78
  atomicshop/archiver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  atomicshop/archiver/_search_in_zip.py,sha256=dd8qFSvIhcKmtnPj_uYNJFPmMwZp4tZys0kKgTw_ACw,8385
80
- atomicshop/archiver/search_in_archive.py,sha256=tCbYZZ53oJrdjatj8_AYre6AZujuii4wRF42PH9falY,11865
80
+ atomicshop/archiver/search_in_archive.py,sha256=EmWif1EmIy-IpOLgcj-y-Sqrh2M3MtLN94aNQzUhrrQ,12300
81
81
  atomicshop/archiver/sevenz_app_w.py,sha256=BWcJb4f7jZEiETDBKyNLE0f5YLFPQx6B91_ObEIXWf8,3007
82
- atomicshop/archiver/sevenzs.py,sha256=5i_C50-deC1Cz_GQdMGofV2jeMPbbGWAvih-QnA72cg,1370
82
+ atomicshop/archiver/sevenzs.py,sha256=b9rI-nF36ZNawwKsPWOgsnm0p-jYDfD1NYV3eA8LoQ0,2491
83
83
  atomicshop/archiver/shutils.py,sha256=BomnK7zT-nQXA1z0i2R2aTv8eu88wPx7tf2HtOdbmEc,1280
84
84
  atomicshop/archiver/zips.py,sha256=0Z_1MWs7YRiCBVpyaG8llnzRguHSO4R51KDMN3FJZt8,16984
85
85
  atomicshop/basics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -159,7 +159,7 @@ atomicshop/monitor/checks/process_running.py,sha256=x66wd6-l466r8sbRQaIli0yswyGt
159
159
  atomicshop/monitor/checks/url.py,sha256=1PvKt_d7wFg7rDMFpUejAQhj0mqWsmlmrNfjNAV2G4g,4123
160
160
  atomicshop/permissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
161
161
  atomicshop/permissions/permissions.py,sha256=CYTDVOI0jh9ks0ZLnnOuPzppgCszFEc9-92DTkVTYi4,522
162
- atomicshop/permissions/ubuntu_permissions.py,sha256=nMmr8ywwS0or8qJdg4ztMeqB2vppE_aytQTRQrg6lFc,2842
162
+ atomicshop/permissions/ubuntu_permissions.py,sha256=bKxZ0hl6cIOJzU1AF8EaLyscQ957_eOe7lEw4UvqKhY,4634
163
163
  atomicshop/permissions/win_permissions.py,sha256=eDQm1jfK9x_hkbLqIJjFTwfqinAWQ0iSr0kW3XrF1BE,1272
164
164
  atomicshop/process_poller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
165
  atomicshop/process_poller/process_pool.py,sha256=4Qs427qd7OcBxu5PMFU5PTmyuxRy0vgj2GLsRt0IoEw,9565
@@ -262,7 +262,7 @@ atomicshop/wrappers/mongodbw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
262
262
  atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py,sha256=pmI9AwWJ2cv5h8GionSpSJkllg6kfp0M381pk6y4Y5U,4015
263
263
  atomicshop/wrappers/mongodbw/install_mongodb_win.py,sha256=64EUQYx7VuMC3ndO2x3nSErh5NZ_BsqMwGvPcybfC-Q,8499
264
264
  atomicshop/wrappers/mongodbw/mongo_infra.py,sha256=IjEF0jPzQz866MpTm7rnksnyyWQeUT_B2h2DA9ryAio,2034
265
- atomicshop/wrappers/mongodbw/mongodbw.py,sha256=IkEw86QFyVRU-5p5s6_6yupvSxmaQxr59GKNgSEkAm4,52617
265
+ atomicshop/wrappers/mongodbw/mongodbw.py,sha256=ih3Gd45rg_70y4sGeu0eEJ3sJd9tEN4I5IqHZelRZJw,52854
266
266
  atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
267
267
  atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=TKGa3jSlSqZTL2NA0nMkWDFtlkz7rxGGn44ywCg7MN8,5228
268
268
  atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -305,13 +305,13 @@ atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
305
305
  atomicshop/wrappers/socketw/accepter.py,sha256=hZZKVYlF3LOHQJsSIEKXZUf6QXXWm-AtqXZevvaYigE,1732
306
306
  atomicshop/wrappers/socketw/base.py,sha256=zYwFxiEzTcItFi1RZQCMxMTLBvECVUiKwivPYKcu44g,2713
307
307
  atomicshop/wrappers/socketw/certificator.py,sha256=mtWPJ_ew3OSwt0-1W4jaoco1VIY4NRCrMv3mDUxb_Cc,12418
308
- atomicshop/wrappers/socketw/creator.py,sha256=zMWLsOF07vX-xQZR720LeHQVndUT8q-ytdCrKF5tt9I,12835
308
+ atomicshop/wrappers/socketw/creator.py,sha256=aSwfN_IwXXf4Hob35vHXUxD_OPeshZcRDZU2hMyfKs0,13243
309
309
  atomicshop/wrappers/socketw/dns_server.py,sha256=RklzINNuoMQn4PGGQEI5hiAldprbVwwvikY6u9X-jTY,49067
310
310
  atomicshop/wrappers/socketw/exception_wrapper.py,sha256=B-X5SHLSUIWToihH2MKnOB1F4A81_X0DpLLfnYKYbEc,7067
311
311
  atomicshop/wrappers/socketw/get_process.py,sha256=aJC-_qFUv3NgWCSUzDI72E4z8_-VTZE9NVZ0CwUoNlM,5698
312
312
  atomicshop/wrappers/socketw/receiver.py,sha256=LRQO-RIY0ZRjSMGVHLVJAXFTVO1zvjgIKSefEngPFfc,8186
313
313
  atomicshop/wrappers/socketw/sender.py,sha256=aX_K8l_rHjd5AWb8bi5mt8-YTkMYVRDB6DnPqK_XDUE,4754
314
- atomicshop/wrappers/socketw/sni.py,sha256=Nc8WMZZR21o5GXILQLVWbf7OzNPXAfE8trJY153e9Qk,17591
314
+ atomicshop/wrappers/socketw/sni.py,sha256=T9PXROiTYYxrd_7X4Hoj9hoNPXXTQpa2HdvmBJJIoeA,17607
315
315
  atomicshop/wrappers/socketw/socket_client.py,sha256=oa3GwS4OPgokrJE5_Oc4-5_wlXHxSH9J5f2DKebms8k,22035
316
316
  atomicshop/wrappers/socketw/socket_server_tester.py,sha256=Qobmh4XV8ZxLUaw-eW4ESKAbeSLecCKn2OWFzMhadk0,6420
317
317
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=WtylpezgIIBuz-A6PfM0hO1sm9Exd4j3qhDXcFc74-E,35567
@@ -319,8 +319,8 @@ atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0L
319
319
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=fgMzDXI0cybwUEqAxprRmY3lqbh30KAV-jOpoFKT-m8,3395
320
320
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
321
321
  atomicshop/wrappers/winregw/winreg_network.py,sha256=zZQfps-CdODQaTUADbHAwKHr5RUg7BLafnKWBbKaLN4,8728
322
- atomicshop-2.18.5.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
323
- atomicshop-2.18.5.dist-info/METADATA,sha256=jFNipJDLDZS4YVzhPI5lVuaMh1xDHuH2Fohc6wyNjy8,10499
324
- atomicshop-2.18.5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
325
- atomicshop-2.18.5.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
326
- atomicshop-2.18.5.dist-info/RECORD,,
322
+ atomicshop-2.18.7.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
323
+ atomicshop-2.18.7.dist-info/METADATA,sha256=QmsAiLi64OzjBDhhrVaF2nlUNIXoKfHiJ0KVmxfHtG8,10576
324
+ atomicshop-2.18.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
325
+ atomicshop-2.18.7.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
326
+ atomicshop-2.18.7.dist-info/RECORD,,