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 +1 -1
- atomicshop/archiver/search_in_archive.py +14 -5
- atomicshop/archiver/sevenzs.py +43 -14
- atomicshop/filesystem.py +18 -61
- atomicshop/ip_addresses.py +6 -1
- atomicshop/permissions/ubuntu_permissions.py +49 -0
- atomicshop/process.py +13 -2
- atomicshop/wrappers/mongodbw/mongodbw.py +4 -0
- atomicshop/wrappers/socketw/creator.py +12 -4
- atomicshop/wrappers/socketw/sni.py +1 -1
- {atomicshop-2.18.5.dist-info → atomicshop-2.18.7.dist-info}/METADATA +3 -1
- {atomicshop-2.18.5.dist-info → atomicshop-2.18.7.dist-info}/RECORD +15 -15
- {atomicshop-2.18.5.dist-info → atomicshop-2.18.7.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.18.5.dist-info → atomicshop-2.18.7.dist-info}/WHEEL +0 -0
- {atomicshop-2.18.5.dist-info → atomicshop-2.18.7.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -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.
|
|
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.
|
|
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.")
|
atomicshop/archiver/sevenzs.py
CHANGED
|
@@ -4,8 +4,50 @@ from typing import Union
|
|
|
4
4
|
import py7zr
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def
|
|
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:
|
|
1297
|
-
|
|
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
|
-
#
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
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
|
|
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
|
"""
|
atomicshop/ip_addresses.py
CHANGED
|
@@ -2,7 +2,12 @@ import ipaddress
|
|
|
2
2
|
from typing import Union, Literal
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def is_ip_address(
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
323
|
-
atomicshop-2.18.
|
|
324
|
-
atomicshop-2.18.
|
|
325
|
-
atomicshop-2.18.
|
|
326
|
-
atomicshop-2.18.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|