atomicshop 2.15.6__py3-none-any.whl → 2.15.8__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/basics/strings.py +68 -1
- atomicshop/filesystem.py +12 -4
- atomicshop/process.py +33 -13
- atomicshop/wrappers/psutilw/processes.py +19 -0
- atomicshop/wrappers/sysmonw.py +1 -1
- {atomicshop-2.15.6.dist-info → atomicshop-2.15.8.dist-info}/METADATA +1 -1
- {atomicshop-2.15.6.dist-info → atomicshop-2.15.8.dist-info}/RECORD +11 -11
- {atomicshop-2.15.6.dist-info → atomicshop-2.15.8.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.15.6.dist-info → atomicshop-2.15.8.dist-info}/WHEEL +0 -0
- {atomicshop-2.15.6.dist-info → atomicshop-2.15.8.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/basics/strings.py
CHANGED
|
@@ -108,7 +108,7 @@ def is_any_string_from_list_in_string(string_list: list, check_string: str) -> b
|
|
|
108
108
|
return any(test_string in check_string for test_string in string_list)
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
def
|
|
111
|
+
def _match_pattern_against_string(
|
|
112
112
|
pattern: str,
|
|
113
113
|
check_string: str,
|
|
114
114
|
case_insensitive: bool = False,
|
|
@@ -214,6 +214,73 @@ def match_pattern_against_string(
|
|
|
214
214
|
return False
|
|
215
215
|
|
|
216
216
|
|
|
217
|
+
def match_pattern_against_string(
|
|
218
|
+
pattern: str,
|
|
219
|
+
check_string: str,
|
|
220
|
+
case_insensitive: bool = False,
|
|
221
|
+
prefix_suffix: bool = False
|
|
222
|
+
) -> bool:
|
|
223
|
+
"""
|
|
224
|
+
Function checks the 'pattern' against 'check_string' and returns 'True' if pattern matches and 'False' if not.
|
|
225
|
+
|
|
226
|
+
Example:
|
|
227
|
+
pattern_string = "*ffmpeg*full_build.zip"
|
|
228
|
+
check_string = "https://github.com/GyanD/codexffmpeg/releases/download/5.1.2/ffmpeg-5.1.2-full_build.zip"
|
|
229
|
+
match_pattern_against_string(pattern_string, check_string)
|
|
230
|
+
Result:
|
|
231
|
+
True
|
|
232
|
+
|
|
233
|
+
:param pattern: string, can include wildcards as '*'.
|
|
234
|
+
:param check_string: string, to check the pattern against.
|
|
235
|
+
:param case_insensitive: boolean, if 'True' will treat the 'pattern' and 'check_string' as case-insensitive.
|
|
236
|
+
:param prefix_suffix: boolean, that sets if the function should return 'True' also for all the cases that wildcard
|
|
237
|
+
in the beginning of the pattern and in the end of the pattern, since the default behavior of regex to return
|
|
238
|
+
'False' on these cases.
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
pattern: *test
|
|
242
|
+
check_string: testblabla
|
|
243
|
+
Default regex behavior will return 'False' with 'prefix_suffix' switch set to 'True',
|
|
244
|
+
this case will return 'True'. Same will go for:
|
|
245
|
+
pattern: test*
|
|
246
|
+
check_string: blablatest
|
|
247
|
+
|
|
248
|
+
Why this is good?
|
|
249
|
+
Let's say you have a python script 'example.py' and you want to find all the executed command lines,
|
|
250
|
+
and you want to make sure that this was executed by 'python'. Your python is installed
|
|
251
|
+
in 'c:\\Python310\\python.exe', and you want to match all the possible patterns of 'python' and 'example.py'.
|
|
252
|
+
Pattern:
|
|
253
|
+
*python*example.py
|
|
254
|
+
You want to match 'True' for the next cases:
|
|
255
|
+
python example.py
|
|
256
|
+
c:\\Python310\\python.exe example.py
|
|
257
|
+
|
|
258
|
+
Default regex behavior is to return 'False' on 'python example.py'.
|
|
259
|
+
|
|
260
|
+
:return: boolean.
|
|
261
|
+
"""
|
|
262
|
+
# Determine the regex flags based on case_insensitive.
|
|
263
|
+
flags = re.IGNORECASE if case_insensitive else 0
|
|
264
|
+
|
|
265
|
+
# Escape the pattern for regex, then replace '*' with '.*' to match any characters.
|
|
266
|
+
escaped_pattern = re.escape(pattern).replace(r'\*', '.*')
|
|
267
|
+
|
|
268
|
+
# Adjust the pattern to match from the start and/or end based on prefix_suffix.
|
|
269
|
+
if prefix_suffix:
|
|
270
|
+
if not pattern.startswith('*'):
|
|
271
|
+
escaped_pattern = '.*' + escaped_pattern
|
|
272
|
+
if not pattern.endswith('*'):
|
|
273
|
+
escaped_pattern = escaped_pattern + '.*'
|
|
274
|
+
else:
|
|
275
|
+
escaped_pattern = '^' + escaped_pattern + '$'
|
|
276
|
+
|
|
277
|
+
# Compile the regex pattern with the appropriate flags.
|
|
278
|
+
regex_pattern = re.compile(escaped_pattern, flags)
|
|
279
|
+
|
|
280
|
+
# Perform the search and return the result.
|
|
281
|
+
return bool(regex_pattern.search(check_string))
|
|
282
|
+
|
|
283
|
+
|
|
217
284
|
def match_list_of_patterns_against_string(
|
|
218
285
|
patterns: list,
|
|
219
286
|
check_string: str,
|
atomicshop/filesystem.py
CHANGED
|
@@ -6,7 +6,7 @@ import shutil
|
|
|
6
6
|
import stat
|
|
7
7
|
import errno
|
|
8
8
|
from contextlib import contextmanager
|
|
9
|
-
from typing import Literal
|
|
9
|
+
from typing import Literal, Union
|
|
10
10
|
import tempfile
|
|
11
11
|
|
|
12
12
|
import psutil
|
|
@@ -1412,12 +1412,16 @@ def backup_folder(directory_path: str, backup_directory: str) -> None:
|
|
|
1412
1412
|
move_folder(directory_path, backup_directory_path)
|
|
1413
1413
|
|
|
1414
1414
|
|
|
1415
|
-
def backup_file(
|
|
1415
|
+
def backup_file(
|
|
1416
|
+
file_path: str,
|
|
1417
|
+
backup_directory: str,
|
|
1418
|
+
timestamp_as_prefix: bool = False
|
|
1419
|
+
) -> Union[str, None]:
|
|
1416
1420
|
"""
|
|
1417
1421
|
Backup the specified file.
|
|
1418
1422
|
|
|
1419
|
-
:param file_path: The file path to
|
|
1420
|
-
:param backup_directory: The directory to
|
|
1423
|
+
:param file_path: The file path to back up.
|
|
1424
|
+
:param backup_directory: The directory to back up the file to.
|
|
1421
1425
|
:param timestamp_as_prefix: boolean, if
|
|
1422
1426
|
True, then the timestamp will be added as a prefix to the file name.
|
|
1423
1427
|
False, then the timestamp will be added as a suffix to the file name.
|
|
@@ -1454,6 +1458,10 @@ def backup_file(file_path: str, backup_directory: str, timestamp_as_prefix: bool
|
|
|
1454
1458
|
backup_file_path: str = str(Path(backup_directory) / file_name)
|
|
1455
1459
|
move_file(file_path, backup_file_path)
|
|
1456
1460
|
|
|
1461
|
+
return backup_file_path
|
|
1462
|
+
else:
|
|
1463
|
+
return None
|
|
1464
|
+
|
|
1457
1465
|
|
|
1458
1466
|
def find_file(file_name: str, directory_path: str):
|
|
1459
1467
|
"""
|
atomicshop/process.py
CHANGED
|
@@ -9,11 +9,16 @@ from .print_api import print_api
|
|
|
9
9
|
from .inspect_wrapper import get_target_function_default_args_and_combine_with_current
|
|
10
10
|
from .basics import strings
|
|
11
11
|
from .wrappers import ubuntu_terminal
|
|
12
|
+
from .wrappers.psutilw import processes
|
|
12
13
|
|
|
13
14
|
if os.name == 'nt':
|
|
14
15
|
from . import get_process_list
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
class MultipleProcessesFound(Exception):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
17
22
|
def is_command_exists(cmd: str) -> bool:
|
|
18
23
|
"""
|
|
19
24
|
The function checks if the command exists in the system.
|
|
@@ -247,25 +252,28 @@ def safe_terminate(popen_process: subprocess.Popen):
|
|
|
247
252
|
popen_process.wait()
|
|
248
253
|
|
|
249
254
|
|
|
250
|
-
def
|
|
255
|
+
def get_running_processes_by_cmdline_pattern(
|
|
251
256
|
pattern: str,
|
|
252
|
-
|
|
257
|
+
cmdline_case_insensitive: bool = False,
|
|
253
258
|
first: bool = False,
|
|
254
259
|
prefix_suffix: bool = False
|
|
255
|
-
):
|
|
260
|
+
) -> list:
|
|
256
261
|
"""
|
|
257
262
|
The function matches specified string pattern including wildcards against all the currently running processes'
|
|
258
263
|
command lines.
|
|
259
264
|
|
|
260
265
|
:param pattern: string, the pattern that we will search in the command line list of currently running processes.
|
|
261
|
-
:param
|
|
266
|
+
:param cmdline_case_insensitive: boolean,
|
|
267
|
+
True, the pattern and the command line will be matched case-insensitive.
|
|
262
268
|
:param first: boolean, that will set if first pattern match found the iteration will stop, or we will return
|
|
263
269
|
the list of all command lines that contain the pattern.
|
|
264
270
|
:param prefix_suffix: boolean. Check the description in 'match_pattern_against_string' function.
|
|
271
|
+
|
|
272
|
+
:return: list, of command lines that contain the pattern.
|
|
265
273
|
"""
|
|
266
274
|
|
|
267
275
|
# Get the list of all the currently running processes.
|
|
268
|
-
get_process_list_instance = get_process_list.GetProcessList(get_method='
|
|
276
|
+
get_process_list_instance = get_process_list.GetProcessList(get_method='psutil', connect_on_init=True)
|
|
269
277
|
processes = get_process_list_instance.get_processes(as_dict=False)
|
|
270
278
|
|
|
271
279
|
# Iterate through all the current process, while fetching executable file 'name' and the command line.
|
|
@@ -273,18 +281,30 @@ def match_pattern_against_running_processes_cmdlines(
|
|
|
273
281
|
matched_cmdlines: list = list()
|
|
274
282
|
for process in processes:
|
|
275
283
|
# Check if command line isn't empty and that string pattern is matched against command line.
|
|
276
|
-
if process['cmdline']
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
+
if process['cmdline']:
|
|
285
|
+
is_pattern_matched = strings.match_pattern_against_string(
|
|
286
|
+
pattern, process['cmdline'], case_insensitive=cmdline_case_insensitive, prefix_suffix=prefix_suffix)
|
|
287
|
+
if is_pattern_matched:
|
|
288
|
+
matched_cmdlines.append(process)
|
|
289
|
+
# If 'first' was set to 'True' we will stop, since we found the first match.
|
|
290
|
+
if first:
|
|
291
|
+
break
|
|
284
292
|
|
|
285
293
|
return matched_cmdlines
|
|
286
294
|
|
|
287
295
|
|
|
296
|
+
def kill_process_by_filename_pattern(pattern: str):
|
|
297
|
+
running_processes = get_running_processes_by_cmdline_pattern(
|
|
298
|
+
pattern, first=False, prefix_suffix=True, cmdline_case_insensitive=True)
|
|
299
|
+
|
|
300
|
+
processes_found: int = len(running_processes)
|
|
301
|
+
|
|
302
|
+
if processes_found > 1:
|
|
303
|
+
raise MultipleProcessesFound(f"[{processes_found}] processes found with pattern '{pattern}'.")
|
|
304
|
+
elif processes_found == 1:
|
|
305
|
+
processes.kill_process_by_pid(running_processes[0]['pid'])
|
|
306
|
+
|
|
307
|
+
|
|
288
308
|
def run_powershell_command(command):
|
|
289
309
|
try:
|
|
290
310
|
result = subprocess.run(["powershell", "-Command", command], capture_output=True, text=True, check=True)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import psutil
|
|
2
2
|
import time
|
|
3
3
|
|
|
4
|
+
from ...print_api import print_api
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
def wait_for_process(pid: int):
|
|
6
8
|
"""
|
|
@@ -24,3 +26,20 @@ def wait_for_process(pid: int):
|
|
|
24
26
|
print(f"No process found with PID {pid}")
|
|
25
27
|
except psutil.AccessDenied:
|
|
26
28
|
print(f"Access denied to process with PID {pid}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def kill_process_by_pid(pid: int, print_kwargs: dict = None):
|
|
32
|
+
try:
|
|
33
|
+
print_api(f"Terminating process: {pid}.", **(print_kwargs or {}))
|
|
34
|
+
proc = psutil.Process(pid)
|
|
35
|
+
proc.terminate() # or proc.kill() if you want to forcefully kill it
|
|
36
|
+
proc.wait(timeout=5) # Wait up to 5 seconds for the process to terminate
|
|
37
|
+
except psutil.NoSuchProcess:
|
|
38
|
+
# print(f"No process found with PID {pid}.")
|
|
39
|
+
pass
|
|
40
|
+
except psutil.AccessDenied:
|
|
41
|
+
# print(f"Access denied to terminate process with PID {pid}.")
|
|
42
|
+
pass
|
|
43
|
+
except psutil.TimeoutExpired:
|
|
44
|
+
# print(f"Process {pid} did not terminate in time.")
|
|
45
|
+
pass
|
atomicshop/wrappers/sysmonw.py
CHANGED
|
@@ -48,7 +48,7 @@ def is_sysmon_running():
|
|
|
48
48
|
:return: boolean, True if Sysmon is running, False otherwise.
|
|
49
49
|
"""
|
|
50
50
|
|
|
51
|
-
process_list: list = process.
|
|
51
|
+
process_list: list = process.get_running_processes_by_cmdline_pattern(
|
|
52
52
|
pattern=SYSMON_FILE_NAME, first=True, process_name_case_insensitive=True)
|
|
53
53
|
|
|
54
54
|
if process_list:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=qXSapgKOZW5gBDxm1PRQP-ON2rt7bHb-DROkBl2tcpQ,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,7 +14,7 @@ atomicshop/dns.py,sha256=h4uZKoz4wbBlLOOduL1GtRcTm-YpiPnGOEGxUm7hhOI,2140
|
|
|
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=Y2_ZplT591KpKDYjVdN9HF1g4eyifWFu7Sm65rpqy7c,55148
|
|
18
18
|
atomicshop/functions.py,sha256=pK8hoCE9z61PtWCxQJsda7YAphrLH1wxU5x-1QJP-sY,499
|
|
19
19
|
atomicshop/get_process_list.py,sha256=hi1NOG-i8S6EcyQ6LTfP4pdxqRfjEijz9SZ5nEbcM9Q,6076
|
|
20
20
|
atomicshop/get_process_name_cmd_dll.py,sha256=CtaSp3mgxxJKCCVW8BLx6BJNx4giCklU_T7USiCEwfc,5162
|
|
@@ -27,7 +27,7 @@ atomicshop/on_exit.py,sha256=Wf1iy2e0b0Zu7oRxrct3VkLdQ_x9B32-z_JerKTt9Z0,5493
|
|
|
27
27
|
atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erXXdjwtS4,4574
|
|
28
28
|
atomicshop/permissions.py,sha256=P6tiUKV-Gw-c3ePEVsst9bqWaHJbB4ZlJB4xbDYVpEs,4436
|
|
29
29
|
atomicshop/print_api.py,sha256=j0bZ9b2rFKCcr0TVx1ARraVKeEs6JaaSgIlBdndy1nI,11600
|
|
30
|
-
atomicshop/process.py,sha256=
|
|
30
|
+
atomicshop/process.py,sha256=PeLvyixXaCfftdUF3oMbohI1L4MdLtvQVDx2V1Tz_Rk,16662
|
|
31
31
|
atomicshop/python_file_patcher.py,sha256=kd3rBWvTcosLEk-7TycNdfKW9fZbe161iVwmH4niUo0,5515
|
|
32
32
|
atomicshop/python_functions.py,sha256=zJg4ogUwECxrDD7xdDN5JikIUctITM5lsyabr_ZNsRw,4435
|
|
33
33
|
atomicshop/question_answer_engine.py,sha256=7nM6kGDSFjQNi87b87-kP9lYM0vTjBHn1rEQGNAfdGA,825
|
|
@@ -100,7 +100,7 @@ atomicshop/basics/multiprocesses.py,sha256=nSskxJSlEdalPM_Uf8cc9kAYYlVwYM1GonBLA
|
|
|
100
100
|
atomicshop/basics/numbers.py,sha256=ESX0z_7o_ok3sOmCKAUBoZinATklgMy2v-4RndqXlVM,1837
|
|
101
101
|
atomicshop/basics/package_module.py,sha256=fBd0uVgFce25ZCVtLq83iyowRlbwdWYFj_t4Ml7LU14,391
|
|
102
102
|
atomicshop/basics/randoms.py,sha256=DmYLtnIhDK29tAQrGP1Nt-A-v8WC7WIEB8Edi-nk3N4,282
|
|
103
|
-
atomicshop/basics/strings.py,sha256=
|
|
103
|
+
atomicshop/basics/strings.py,sha256=YIE0q5mdpwPtGXsRgvn-HSvR3R6sU1EyBCWHQq3IlKs,21216
|
|
104
104
|
atomicshop/basics/threads.py,sha256=xvgdDJdmgN0wmmARoZ-H7Kvl1GOcEbvgaeGL4M3Hcx8,2819
|
|
105
105
|
atomicshop/basics/timeit_template.py,sha256=fYLrk-X_dhdVtnPU22tarrhhvlggeW6FdKCXM8zkX68,405
|
|
106
106
|
atomicshop/basics/tracebacks.py,sha256=cNfh_oAwF55kSIdqtv3boHZQIoQI8TajxkTnwJwpweI,535
|
|
@@ -181,7 +181,7 @@ atomicshop/wrappers/pipw.py,sha256=mu4jnHkSaYNfpBiLZKMZxEX_E2LqW5BVthMZkblPB_c,1
|
|
|
181
181
|
atomicshop/wrappers/process_wrapper_pbtk.py,sha256=ycPmBRnv627RWks6N8OhxJQe8Gu3h3Vwj-4HswPOw0k,599
|
|
182
182
|
atomicshop/wrappers/pycharmw.py,sha256=OHcaVlyhIqhgRioPhkeS5krDZ_NezZjpBCvyRiLjWwI,2723
|
|
183
183
|
atomicshop/wrappers/pyopensslw.py,sha256=OBWxA6EJ2vU_Qlf4M8m6ilcG3hyYB4yB0EsXUf7NhEU,6804
|
|
184
|
-
atomicshop/wrappers/sysmonw.py,sha256=
|
|
184
|
+
atomicshop/wrappers/sysmonw.py,sha256=MFF8ts0gHbXn2_QeH196UncOUtm4MnM2cQBzTOnfrnk,5351
|
|
185
185
|
atomicshop/wrappers/ubuntu_terminal.py,sha256=BBZD3EH6KSDORd5IZBZM-ti4U6Qh1sZwftx42s7hqB4,10917
|
|
186
186
|
atomicshop/wrappers/wslw.py,sha256=AKphiHLSddL7ErevUowr3f9Y1AgGz_R3KZ3NssW07h8,6959
|
|
187
187
|
atomicshop/wrappers/certauthw/certauth.py,sha256=hKedW0DOWlEigSNm8wu4SqHkCQsGJ1tJfH7s4nr3Bk0,12223
|
|
@@ -265,7 +265,7 @@ atomicshop/wrappers/psutilw/cpus.py,sha256=w6LPBMINqS-T_X8vzdYkLS2Wzuve28Ydp_Gaf
|
|
|
265
265
|
atomicshop/wrappers/psutilw/disks.py,sha256=3ZSVoommKH1TWo37j_83frB-NqXF4Nf5q5mBCX8G4jE,9221
|
|
266
266
|
atomicshop/wrappers/psutilw/memories.py,sha256=_S0aL8iaoIHebd1vOFrY_T9aROM5Jx2D5CvDh_4j0Vc,528
|
|
267
267
|
atomicshop/wrappers/psutilw/networks.py,sha256=jC53QXKdZQPCLdy_iNWXeq-CwpW7H6va6bFPRmI_e7A,1507
|
|
268
|
-
atomicshop/wrappers/psutilw/processes.py,sha256=
|
|
268
|
+
atomicshop/wrappers/psutilw/processes.py,sha256=ZOkqEEF36NFeQg7G-baUUdTaV8DQe_jpkJg42ASUuc0,1566
|
|
269
269
|
atomicshop/wrappers/psutilw/psutilw.py,sha256=q3EwgprqyrR4zLCjl4l5DHFOQoukEvQMIPjNB504oQ0,21262
|
|
270
270
|
atomicshop/wrappers/psycopgw/psycopgw.py,sha256=XJvVf0oAUjCHkrYfKeFuGCpfn0Oxj3u4SbKMKA1508E,7118
|
|
271
271
|
atomicshop/wrappers/pywin32w/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -294,8 +294,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
|
|
|
294
294
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
295
295
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
296
296
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
|
|
297
|
-
atomicshop-2.15.
|
|
298
|
-
atomicshop-2.15.
|
|
299
|
-
atomicshop-2.15.
|
|
300
|
-
atomicshop-2.15.
|
|
301
|
-
atomicshop-2.15.
|
|
297
|
+
atomicshop-2.15.8.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
298
|
+
atomicshop-2.15.8.dist-info/METADATA,sha256=3DmLuxpwvuPD30x8sV91qGlIySBzcaNhAjiX_qbBd-8,10502
|
|
299
|
+
atomicshop-2.15.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
300
|
+
atomicshop-2.15.8.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
301
|
+
atomicshop-2.15.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|