atomicshop 2.5.16__py3-none-any.whl → 2.6.1__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/enums.py +2 -2
- atomicshop/basics/multiprocesses.py +333 -0
- atomicshop/basics/numbers.py +13 -0
- atomicshop/file_io/docxs.py +4 -4
- atomicshop/filesystem.py +41 -25
- atomicshop/mitm/initialize_mitm_server.py +3 -4
- atomicshop/system_resources.py +26 -0
- atomicshop/wrappers/loggingw/loggingw.py +18 -0
- atomicshop/wrappers/loggingw/reading.py +6 -7
- atomicshop/wrappers/socketw/socket_server_tester.py +5 -5
- {atomicshop-2.5.16.dist-info → atomicshop-2.6.1.dist-info}/METADATA +1 -1
- {atomicshop-2.5.16.dist-info → atomicshop-2.6.1.dist-info}/RECORD +16 -15
- {atomicshop-2.5.16.dist-info → atomicshop-2.6.1.dist-info}/WHEEL +1 -1
- {atomicshop-2.5.16.dist-info → atomicshop-2.6.1.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.5.16.dist-info → atomicshop-2.6.1.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/basics/enums.py
CHANGED
|
@@ -32,10 +32,10 @@ def __comparison_usage_example(
|
|
|
32
32
|
main folder.
|
|
33
33
|
|
|
34
34
|
Usage:
|
|
35
|
-
from atomicshop.filesystem import
|
|
35
|
+
from atomicshop.filesystem import get_file_paths_from_directory, ComparisonOperator
|
|
36
36
|
|
|
37
37
|
# Get full paths of all the 'engine_config.ini' files.
|
|
38
|
-
engine_config_path_list =
|
|
38
|
+
engine_config_path_list = get_file_paths_from_directory(
|
|
39
39
|
directory_fullpath=some_directory_path,
|
|
40
40
|
file_name_check_tuple=(config_file_name, ComparisonOperator.EQ))
|
|
41
41
|
"""
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import multiprocessing
|
|
2
2
|
import queue
|
|
3
|
+
from concurrent.futures import ProcessPoolExecutor, as_completed
|
|
4
|
+
|
|
5
|
+
from ..import system_resources
|
|
3
6
|
|
|
4
7
|
|
|
5
8
|
def process_wrap_queue(function_reference, *args, **kwargs):
|
|
@@ -28,3 +31,333 @@ def process_wrap_queue(function_reference, *args, **kwargs):
|
|
|
28
31
|
process_object.join()
|
|
29
32
|
|
|
30
33
|
return queue_object.get()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MultiProcessorRecursive:
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
process_function,
|
|
40
|
+
input_list: list,
|
|
41
|
+
max_workers: int = None,
|
|
42
|
+
cpu_percent_max: int = 80,
|
|
43
|
+
memory_percent_max: int = 80,
|
|
44
|
+
wait_time: float = 5
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
MultiProcessor class. Used to execute functions in parallel. The result of each execution is feeded back
|
|
48
|
+
to the provided function. Making it sort of recursive execution.
|
|
49
|
+
:param process_function: function, function to execute on the input list.
|
|
50
|
+
:param input_list: list, list of inputs to process.
|
|
51
|
+
:param max_workers: integer, number of workers to execute functions in parallel. Default is None, which
|
|
52
|
+
is the number of CPUs.
|
|
53
|
+
:param cpu_percent_max: integer, maximum CPU percentage. Above that usage, we will wait before starting new
|
|
54
|
+
execution.
|
|
55
|
+
:param memory_percent_max: integer, maximum memory percentage. Above that usage, we will wait, before starting
|
|
56
|
+
new execution.
|
|
57
|
+
:param wait_time: float, time to wait if the CPU or memory usage is above the maximum percentage.
|
|
58
|
+
|
|
59
|
+
Usage:
|
|
60
|
+
def unpack_file(file_path):
|
|
61
|
+
# Process the file at file_path and unpack it.
|
|
62
|
+
# Return a list of new file paths that were extracted from the provided path.
|
|
63
|
+
return [new_file_path1, new_file_path2] # Example return value
|
|
64
|
+
|
|
65
|
+
# List of file paths to process
|
|
66
|
+
file_paths = ["path1", "path2", "path3"]
|
|
67
|
+
|
|
68
|
+
# Create an instance of MultiProcessor
|
|
69
|
+
# Note: unpacking.unpack_file is passed without parentheses
|
|
70
|
+
processor = MultiProcessor(
|
|
71
|
+
process_function=unpack_file,
|
|
72
|
+
input_list=file_paths,
|
|
73
|
+
max_workers=4, # Number of parallel workers
|
|
74
|
+
cpu_percent_max=80, # Max CPU usage percentage
|
|
75
|
+
memory_percent_max=80, # Max memory usage percentage
|
|
76
|
+
wait_time=5 # Time to wait if resources are overused
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Run the processing
|
|
80
|
+
processor.run_process()
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
self.process_function = process_function
|
|
84
|
+
self.input_list: list = input_list
|
|
85
|
+
self.max_workers: int = max_workers
|
|
86
|
+
self.cpu_percent_max: int = cpu_percent_max
|
|
87
|
+
self.memory_percent_max: int = memory_percent_max
|
|
88
|
+
self.wait_time: float = wait_time
|
|
89
|
+
|
|
90
|
+
def run_process(self):
|
|
91
|
+
with multiprocessing.Pool(processes=self.max_workers) as pool:
|
|
92
|
+
# Keep track of the async results
|
|
93
|
+
async_results = []
|
|
94
|
+
|
|
95
|
+
while self.input_list:
|
|
96
|
+
new_input_list = []
|
|
97
|
+
for item in self.input_list:
|
|
98
|
+
# Check system resources before processing each item
|
|
99
|
+
system_resources.wait_for_resource_availability(
|
|
100
|
+
cpu_percent_max=self.cpu_percent_max,
|
|
101
|
+
memory_percent_max=self.memory_percent_max,
|
|
102
|
+
wait_time=self.wait_time)
|
|
103
|
+
|
|
104
|
+
# Process the item
|
|
105
|
+
async_result = pool.apply_async(self.process_function, (item,))
|
|
106
|
+
async_results.append(async_result)
|
|
107
|
+
|
|
108
|
+
# Reset input_list for next round of processing
|
|
109
|
+
self.input_list = []
|
|
110
|
+
|
|
111
|
+
# Collect results as they complete
|
|
112
|
+
for async_result in async_results:
|
|
113
|
+
try:
|
|
114
|
+
result = async_result.get()
|
|
115
|
+
# Assuming process_function returns a list, extend new_input_list
|
|
116
|
+
new_input_list.extend(result)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f"An error occurred: {e}")
|
|
119
|
+
|
|
120
|
+
# Update the input_list for the next iteration
|
|
121
|
+
self.input_list = new_input_list
|
|
122
|
+
# Clear the async_results for the next iteration
|
|
123
|
+
async_results.clear()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class _MultiProcessorTest:
|
|
127
|
+
def __init__(self, worker_count: int = 8, initialize_at_start: bool = True):
|
|
128
|
+
"""
|
|
129
|
+
MultiProcessor class. Used to execute functions in parallel.
|
|
130
|
+
:param worker_count: integer, number of workers to execute functions in parallel.
|
|
131
|
+
:param initialize_at_start: boolean, if True, the workers will be initialized at the start of the class.
|
|
132
|
+
|
|
133
|
+
Usage:
|
|
134
|
+
# Example functions
|
|
135
|
+
def my_function1(x):
|
|
136
|
+
return x * x
|
|
137
|
+
|
|
138
|
+
def my_callback(result):
|
|
139
|
+
print(f"Result received: {result}")
|
|
140
|
+
|
|
141
|
+
# Usage example
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
processor = MultiProcessor()
|
|
144
|
+
|
|
145
|
+
# Adding tasks with callback (asynchronous)
|
|
146
|
+
for i in range(5):
|
|
147
|
+
processor.add_task(my_function1, i, callback=my_callback)
|
|
148
|
+
|
|
149
|
+
# Adding a task and waiting for the result (synchronous)
|
|
150
|
+
result = processor.add_task(my_function1, 10, wait=True)
|
|
151
|
+
print(f"Synchronous result: {result}")
|
|
152
|
+
|
|
153
|
+
# Shutdown and wait for all tasks to complete
|
|
154
|
+
processor.shutdown()
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
self.worker_count: int = worker_count
|
|
158
|
+
self.task_queue = multiprocessing.Queue()
|
|
159
|
+
self.result_queue = multiprocessing.Queue()
|
|
160
|
+
self.workers: list = list()
|
|
161
|
+
|
|
162
|
+
if initialize_at_start:
|
|
163
|
+
self._init_workers()
|
|
164
|
+
|
|
165
|
+
def _worker(self):
|
|
166
|
+
while True:
|
|
167
|
+
task = self.task_queue.get()
|
|
168
|
+
if task is None: # Termination signal
|
|
169
|
+
break
|
|
170
|
+
func, args, kwargs, callback, return_result = task
|
|
171
|
+
try:
|
|
172
|
+
result = func(*args, **kwargs)
|
|
173
|
+
if callback:
|
|
174
|
+
callback(result)
|
|
175
|
+
if return_result:
|
|
176
|
+
self.result_queue.put(result)
|
|
177
|
+
except Exception as e:
|
|
178
|
+
if callback:
|
|
179
|
+
callback(e)
|
|
180
|
+
if return_result:
|
|
181
|
+
self.result_queue.put(e)
|
|
182
|
+
|
|
183
|
+
def _init_workers(self):
|
|
184
|
+
for _ in range(self.worker_count):
|
|
185
|
+
p = multiprocessing.Process(target=self._worker)
|
|
186
|
+
p.start()
|
|
187
|
+
self.workers.append(p)
|
|
188
|
+
|
|
189
|
+
def add_task(self, func, *args, callback=None, wait=False, **kwargs):
|
|
190
|
+
"""
|
|
191
|
+
Add a task to the queue.
|
|
192
|
+
:param func: reference, function to execute.
|
|
193
|
+
:param args: arguments for the function.
|
|
194
|
+
:param callback: reference, function to execute after the task is completed. The result of the task will be
|
|
195
|
+
passed to the callback function.
|
|
196
|
+
:param wait: boolean, if True, the function will wait for the task to complete and return the result.
|
|
197
|
+
:param kwargs: keyword arguments for the function.
|
|
198
|
+
:return:
|
|
199
|
+
"""
|
|
200
|
+
if wait:
|
|
201
|
+
self.task_queue.put((func, args, kwargs, None, True))
|
|
202
|
+
return self.result_queue.get()
|
|
203
|
+
else:
|
|
204
|
+
self.task_queue.put((func, args, kwargs, callback, False))
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
def shutdown(self):
|
|
208
|
+
# Signal workers to exit
|
|
209
|
+
for _ in self.workers:
|
|
210
|
+
self.task_queue.put(None)
|
|
211
|
+
|
|
212
|
+
# Wait for all workers to finish
|
|
213
|
+
for p in self.workers:
|
|
214
|
+
p.join()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class _MultiConcuratorTest:
|
|
218
|
+
def __init__(self, max_workers: int = 2, initialize_at_start: bool = False):
|
|
219
|
+
"""
|
|
220
|
+
MultiConcurator class. Used to execute functions in parallel with the ProcessPoolExecutor.
|
|
221
|
+
|
|
222
|
+
:param max_workers: integer, number of workers to execute functions in parallel.
|
|
223
|
+
:param initialize_at_start: boolean, if True, the workers will be initialized at the start of the class.
|
|
224
|
+
|
|
225
|
+
Usage 1:
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
|
|
228
|
+
|
|
229
|
+
# ... add tasks to concurator ...
|
|
230
|
+
|
|
231
|
+
concurator.wait_for_completion()
|
|
232
|
+
|
|
233
|
+
# Continuously get and process results
|
|
234
|
+
for result in concurator.get_results():
|
|
235
|
+
print(result)
|
|
236
|
+
|
|
237
|
+
concurator.shutdown()
|
|
238
|
+
|
|
239
|
+
Usage 2:
|
|
240
|
+
# Basic Usage - Executing Functions in Parallel
|
|
241
|
+
This example shows how to execute a simple function in parallel.
|
|
242
|
+
|
|
243
|
+
def square(number):
|
|
244
|
+
return number * number
|
|
245
|
+
|
|
246
|
+
if __name__ == "__main__":
|
|
247
|
+
concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
|
|
248
|
+
|
|
249
|
+
# Submit tasks
|
|
250
|
+
for num in range(10):
|
|
251
|
+
concurator.add_to_queue(square, num)
|
|
252
|
+
|
|
253
|
+
# Wait for completion and handle results
|
|
254
|
+
concurator.wait_for_completion()
|
|
255
|
+
|
|
256
|
+
# Retrieve results
|
|
257
|
+
while not concurator.queue.empty():
|
|
258
|
+
result = concurator.queue.get()
|
|
259
|
+
print(result)
|
|
260
|
+
|
|
261
|
+
concurator.shutdown()
|
|
262
|
+
|
|
263
|
+
Usage 3:
|
|
264
|
+
# Handling Results with a Callback Function
|
|
265
|
+
# This example demonstrates handling the results of each task using a callback function.
|
|
266
|
+
|
|
267
|
+
def multiply(x, y):
|
|
268
|
+
return x * y
|
|
269
|
+
|
|
270
|
+
def handle_result(result):
|
|
271
|
+
print(f"Result: {result}")
|
|
272
|
+
|
|
273
|
+
if __name__ == "__main__":
|
|
274
|
+
concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
|
|
275
|
+
|
|
276
|
+
for i in range(5):
|
|
277
|
+
concurator.add_to_queue(multiply, i, i+1)
|
|
278
|
+
|
|
279
|
+
concurator.wait_for_completion(handle_result)
|
|
280
|
+
concurator.shutdown()
|
|
281
|
+
|
|
282
|
+
Usage 4:
|
|
283
|
+
# Dynamically Initializing the Executor
|
|
284
|
+
# This example shows how to initialize the executor dynamically and process tasks.
|
|
285
|
+
|
|
286
|
+
def compute_power(base, exponent):
|
|
287
|
+
return base ** exponent
|
|
288
|
+
|
|
289
|
+
if __name__ == "__main__":
|
|
290
|
+
concurator = MultiConcurator(max_workers=3)
|
|
291
|
+
|
|
292
|
+
# Initialize executor when needed
|
|
293
|
+
concurator.init_executor()
|
|
294
|
+
|
|
295
|
+
for i in range(1, 5):
|
|
296
|
+
concurator.add_to_queue(compute_power, i, 2)
|
|
297
|
+
|
|
298
|
+
concurator.wait_for_completion()
|
|
299
|
+
while not concurator.queue.empty():
|
|
300
|
+
print(concurator.queue.get())
|
|
301
|
+
|
|
302
|
+
concurator.shutdown()
|
|
303
|
+
|
|
304
|
+
Usage 5:
|
|
305
|
+
# Handling Exceptions in Tasks
|
|
306
|
+
# This example demonstrates how you can handle exceptions that might occur in the tasks.
|
|
307
|
+
|
|
308
|
+
def risky_division(x, y):
|
|
309
|
+
if y == 0:
|
|
310
|
+
raise ValueError("Cannot divide by zero")
|
|
311
|
+
return x / y
|
|
312
|
+
|
|
313
|
+
def handle_result(result):
|
|
314
|
+
if isinstance(result, Exception):
|
|
315
|
+
print(f"Error occurred: {result}")
|
|
316
|
+
else:
|
|
317
|
+
print(f"Result: {result}")
|
|
318
|
+
|
|
319
|
+
if __name__ == "__main__":
|
|
320
|
+
concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
|
|
321
|
+
|
|
322
|
+
concurator.add_to_queue(risky_division, 10, 2)
|
|
323
|
+
concurator.add_to_queue(risky_division, 10, 0) # This will raise an exception
|
|
324
|
+
|
|
325
|
+
concurator.wait_for_completion(handle_result)
|
|
326
|
+
concurator.shutdown()
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
self.executor = None
|
|
330
|
+
self.max_workers: int = max_workers
|
|
331
|
+
self.futures: list = list()
|
|
332
|
+
self.queue = multiprocessing.Queue()
|
|
333
|
+
|
|
334
|
+
if initialize_at_start:
|
|
335
|
+
self.init_executor()
|
|
336
|
+
|
|
337
|
+
def init_executor(self):
|
|
338
|
+
if self.executor is None:
|
|
339
|
+
self.executor = ProcessPoolExecutor(max_workers=self.max_workers)
|
|
340
|
+
|
|
341
|
+
def add_to_queue(self, func, *args, **kwargs):
|
|
342
|
+
if self.executor is None:
|
|
343
|
+
raise RuntimeError("Executor has not been initialized. Call init_executor() first.")
|
|
344
|
+
future = self.executor.submit(func, *args, **kwargs)
|
|
345
|
+
self.futures.append(future)
|
|
346
|
+
return future
|
|
347
|
+
|
|
348
|
+
def wait_for_completion(self, handle_result=None):
|
|
349
|
+
for future in as_completed(self.futures):
|
|
350
|
+
result = future.result()
|
|
351
|
+
if handle_result:
|
|
352
|
+
handle_result(result)
|
|
353
|
+
else:
|
|
354
|
+
self.queue.put(result)
|
|
355
|
+
|
|
356
|
+
def get_results(self):
|
|
357
|
+
while not self.queue.empty():
|
|
358
|
+
yield self.queue.get()
|
|
359
|
+
|
|
360
|
+
def shutdown(self):
|
|
361
|
+
if self.executor is not None:
|
|
362
|
+
self.executor.shutdown()
|
|
363
|
+
self.executor = None
|
atomicshop/basics/numbers.py
CHANGED
|
@@ -5,3 +5,16 @@ def is_divisible(n, k):
|
|
|
5
5
|
return True
|
|
6
6
|
else:
|
|
7
7
|
return False
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_highest_number(numbers: list[float, int, str]):
|
|
11
|
+
"""
|
|
12
|
+
Takes a list of numbers (float or integers) and returns the highest one.
|
|
13
|
+
|
|
14
|
+
:param numbers: List of floats or integers.
|
|
15
|
+
:return: The highest number in the list.
|
|
16
|
+
"""
|
|
17
|
+
if not numbers:
|
|
18
|
+
raise ValueError('The list of numbers is empty.')
|
|
19
|
+
|
|
20
|
+
return max(numbers)
|
atomicshop/file_io/docxs.py
CHANGED
|
@@ -2,7 +2,6 @@ from docx import Document
|
|
|
2
2
|
from docx.opc.exceptions import PackageNotFoundError
|
|
3
3
|
|
|
4
4
|
from .. import filesystem, config_init
|
|
5
|
-
from . import file_io
|
|
6
5
|
from ..print_api import print_api
|
|
7
6
|
|
|
8
7
|
|
|
@@ -67,12 +66,13 @@ def search_for_hyperlink_in_files(directory_path: str, hyperlink: str, relative_
|
|
|
67
66
|
raise NotADirectoryError(f"Directory doesn't exist: {directory_path}")
|
|
68
67
|
|
|
69
68
|
# Get all the docx files in the specified directory.
|
|
70
|
-
files = filesystem.
|
|
71
|
-
directory_path, file_name_check_pattern="*.docx",
|
|
69
|
+
files = filesystem.get_file_paths_from_directory(
|
|
70
|
+
directory_path, file_name_check_pattern="*.docx",
|
|
71
|
+
add_relative_directory=True, relative_file_name_as_directory=True)
|
|
72
72
|
|
|
73
73
|
found_in_files: list = list()
|
|
74
74
|
for file_path in files:
|
|
75
|
-
hyperlinks = get_hyperlinks(file_path['
|
|
75
|
+
hyperlinks = get_hyperlinks(file_path['file_path'])
|
|
76
76
|
if hyperlink in hyperlinks:
|
|
77
77
|
found_in_files.append(file_path)
|
|
78
78
|
|
atomicshop/filesystem.py
CHANGED
|
@@ -381,7 +381,7 @@ def move_files_from_folder_to_folder(source_directory: str, target_directory: st
|
|
|
381
381
|
# Move each item to the destination directory
|
|
382
382
|
shutil.move(source_item, destination_item)
|
|
383
383
|
# # Get all file names without full paths in source folder.
|
|
384
|
-
# file_list_in_source: list =
|
|
384
|
+
# file_list_in_source: list = get_file_paths_from_directory(source_directory)
|
|
385
385
|
#
|
|
386
386
|
# # Iterate through all the files.
|
|
387
387
|
# for file_path in file_list_in_source:
|
|
@@ -438,10 +438,11 @@ def get_file_names_from_directory(directory_path: str) -> list:
|
|
|
438
438
|
return file_list
|
|
439
439
|
|
|
440
440
|
|
|
441
|
-
def
|
|
442
|
-
|
|
441
|
+
def get_file_paths_from_directory(
|
|
442
|
+
directory_path: str,
|
|
443
443
|
recursive: bool = True,
|
|
444
444
|
file_name_check_pattern: str = '*',
|
|
445
|
+
add_relative_directory: bool = False,
|
|
445
446
|
relative_file_name_as_directory: bool = False,
|
|
446
447
|
add_last_modified_time: bool = False,
|
|
447
448
|
sort_by_last_modified_time: bool = False
|
|
@@ -453,13 +454,18 @@ def get_file_paths_and_relative_directories(
|
|
|
453
454
|
If 'file_name_check_tuple' specified, the function will return only list of files that answer to the input
|
|
454
455
|
of that tuple.
|
|
455
456
|
|
|
456
|
-
:param
|
|
457
|
+
:param directory_path: string to full path to directory on the filesystem to scan.
|
|
457
458
|
:param recursive: boolean.
|
|
458
459
|
'True', then the function will scan recursively in subdirectories.
|
|
459
460
|
'False', then the function will scan only in the directory that was passed.
|
|
460
461
|
:param file_name_check_pattern: string, if specified, the function will return only files that match the pattern.
|
|
461
462
|
The string can contain part of file name to check or full file name with extension.
|
|
462
463
|
Can contain wildcards.
|
|
464
|
+
:param add_relative_directory: boolean, if
|
|
465
|
+
'True', then the function will add relative directory to the output list.
|
|
466
|
+
In this case the output list will contain dictionaries with keys 'path' and 'relative_dir'.
|
|
467
|
+
'False', then the function will not add relative directory to the output list.
|
|
468
|
+
And the output list will contain only full file paths.
|
|
463
469
|
:param relative_file_name_as_directory: boolean that will set if 'relative_directory_list' should contain
|
|
464
470
|
file name with extension for each entry.
|
|
465
471
|
:param add_last_modified_time: boolean, if 'True', then the function will add last modified time of the file
|
|
@@ -477,33 +483,43 @@ def get_file_paths_and_relative_directories(
|
|
|
477
483
|
file, against the main path to directory that was passed to the parent function.
|
|
478
484
|
"""
|
|
479
485
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
# Get full file path of the file.
|
|
483
|
-
file_result['path'] = os.path.join(dirpath, file)
|
|
486
|
+
file_path: str = os.path.join(dirpath, file)
|
|
484
487
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
# Output the path with filename.
|
|
488
|
-
file_result['relative_dir'] = _get_relative_output_path_from_input_path(directory_fullpath, dirpath, file)
|
|
489
|
-
# if 'relative_file_name_as_directory' wasn't passed.
|
|
488
|
+
if not add_relative_directory and not add_last_modified_time:
|
|
489
|
+
file_result: str = file_path
|
|
490
490
|
else:
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
491
|
+
file_result: dict = dict()
|
|
492
|
+
|
|
493
|
+
# Get full file path of the file.
|
|
494
|
+
file_result['file_path'] = file_path
|
|
495
|
+
|
|
496
|
+
if add_relative_directory:
|
|
497
|
+
# if 'relative_file_name_as_directory' was passed.
|
|
498
|
+
if relative_file_name_as_directory:
|
|
499
|
+
# Output the path with filename.
|
|
500
|
+
file_result['relative_dir'] = _get_relative_output_path_from_input_path(
|
|
501
|
+
directory_path, dirpath, file)
|
|
502
|
+
# if 'relative_file_name_as_directory' wasn't passed.
|
|
503
|
+
else:
|
|
504
|
+
# Output the path without filename.
|
|
505
|
+
file_result['relative_dir'] = _get_relative_output_path_from_input_path(directory_path, dirpath)
|
|
506
|
+
|
|
507
|
+
# Remove separator from the beginning if exists.
|
|
508
|
+
file_result['relative_dir'] = file_result['relative_dir'].removeprefix(os.sep)
|
|
509
|
+
|
|
510
|
+
# If 'add_last_modified_time' was passed.
|
|
511
|
+
if add_last_modified_time:
|
|
512
|
+
# Get last modified time of the file.
|
|
513
|
+
file_result['last_modified'] = get_file_modified_time(file_result['path'])
|
|
501
514
|
|
|
502
515
|
object_list.append(file_result)
|
|
503
516
|
|
|
504
517
|
if sort_by_last_modified_time and not add_last_modified_time:
|
|
505
518
|
raise ValueError('Parameter "sort_by_last_modified_time" cannot be "True" if parameter '
|
|
506
519
|
'"add_last_modified_time" is not "True".')
|
|
520
|
+
if relative_file_name_as_directory and not add_relative_directory:
|
|
521
|
+
raise ValueError('Parameter "relative_file_name_as_directory" cannot be "True" if parameter '
|
|
522
|
+
'"add_relative_directory" is not "True".')
|
|
507
523
|
|
|
508
524
|
# === Function main ================
|
|
509
525
|
# Define locals.
|
|
@@ -511,7 +527,7 @@ def get_file_paths_and_relative_directories(
|
|
|
511
527
|
|
|
512
528
|
# "Walk" over all the directories and subdirectories - make list of full file paths inside the directory
|
|
513
529
|
# recursively.
|
|
514
|
-
for dirpath, subdirs, files in os.walk(
|
|
530
|
+
for dirpath, subdirs, files in os.walk(directory_path):
|
|
515
531
|
# Iterate through all the file names that were found in the folder.
|
|
516
532
|
for file in files:
|
|
517
533
|
# If 'file_name_check_pattern' was passed.
|
|
@@ -674,7 +690,7 @@ def get_file_hashes_from_directory(directory_path: str, recursive: bool = False,
|
|
|
674
690
|
"""
|
|
675
691
|
|
|
676
692
|
# Get all the files.
|
|
677
|
-
file_paths_list =
|
|
693
|
+
file_paths_list = get_file_paths_from_directory(directory_path, recursive=recursive)
|
|
678
694
|
|
|
679
695
|
# Create a list of dictionaries, each dictionary is a file with its hash.
|
|
680
696
|
files: list = list()
|
|
@@ -6,7 +6,6 @@ from atomicshop.print_api import print_api
|
|
|
6
6
|
from .import_config import ImportConfig
|
|
7
7
|
from .initialize_engines import ModuleCategory
|
|
8
8
|
from .connection_thread_worker import thread_worker_main
|
|
9
|
-
from ..filesystem import get_file_paths_and_relative_directories
|
|
10
9
|
from .. import filesystem, queues
|
|
11
10
|
from ..python_functions import get_current_python_version_string, check_python_version_compliance
|
|
12
11
|
from ..wrappers.socketw.socket_wrapper import SocketWrapper
|
|
@@ -104,8 +103,8 @@ def initialize_mitm_server(config_static):
|
|
|
104
103
|
system_logger.info("Importing engine modules.")
|
|
105
104
|
|
|
106
105
|
# Get full paths of all the 'engine_config.ini' files.
|
|
107
|
-
engine_config_path_list =
|
|
108
|
-
|
|
106
|
+
engine_config_path_list = filesystem.get_file_paths_from_directory(
|
|
107
|
+
directory_path=config_static.ENGINES_DIRECTORY_PATH,
|
|
109
108
|
file_name_check_pattern=config_static.ENGINE_CONFIG_FILE_NAME)
|
|
110
109
|
|
|
111
110
|
# Iterate through all the 'engine_config.ini' file paths.
|
|
@@ -114,7 +113,7 @@ def initialize_mitm_server(config_static):
|
|
|
114
113
|
for engine_config_path in engine_config_path_list:
|
|
115
114
|
# Initialize engine.
|
|
116
115
|
current_module = ModuleCategory(config_static.WORKING_DIRECTORY)
|
|
117
|
-
current_module.fill_engine_fields_from_config(engine_config_path
|
|
116
|
+
current_module.fill_engine_fields_from_config(engine_config_path)
|
|
118
117
|
current_module.initialize_engine(logs_path=config['log']['logs_path'],
|
|
119
118
|
logger=system_logger)
|
|
120
119
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import psutil
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from .print_api import print_api
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def check_system_resources():
|
|
8
|
+
cpu_usage = psutil.cpu_percent(interval=0.1)
|
|
9
|
+
memory_usage = psutil.virtual_memory().percent
|
|
10
|
+
return cpu_usage, memory_usage
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def wait_for_resource_availability(cpu_percent_max: int = 80, memory_percent_max: int = 80, wait_time: float = 5):
|
|
14
|
+
"""
|
|
15
|
+
Wait for system resources to be available.
|
|
16
|
+
:param cpu_percent_max: int, maximum CPU percentage. Above that usage, we will wait.
|
|
17
|
+
:param memory_percent_max: int, maximum memory percentage. Above that usage, we will wait.
|
|
18
|
+
:param wait_time: float, time to wait between checks.
|
|
19
|
+
:return: None
|
|
20
|
+
"""
|
|
21
|
+
while True:
|
|
22
|
+
cpu, memory = check_system_resources()
|
|
23
|
+
if cpu < cpu_percent_max and memory < memory_percent_max:
|
|
24
|
+
break
|
|
25
|
+
print_api(f"Waiting for resources to be available... CPU: {cpu}%, Memory: {memory}%", color='yellow')
|
|
26
|
+
time.sleep(wait_time) # Wait for 'wait_time' seconds before checking again
|
|
@@ -231,3 +231,21 @@ def start_queue_listener_for_file_handler_and_get_queue_handler(file_handler):
|
|
|
231
231
|
handlers.start_queue_listener_for_file_handler(file_handler, queue_object)
|
|
232
232
|
|
|
233
233
|
return handlers.get_queue_handler(queue_object)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def disable_default_logger():
|
|
237
|
+
"""
|
|
238
|
+
Function to disable default logger.
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
# # Get the default logger.
|
|
242
|
+
# logger = logging.getLogger()
|
|
243
|
+
# # Remove all handlers from the logger.
|
|
244
|
+
# logger.handlers.clear()
|
|
245
|
+
# # Set the logger level to 'NOTSET'.
|
|
246
|
+
# logger.setLevel(logging.NOTSET)
|
|
247
|
+
# # Disable propagation from the 'root' logger, so we will not see the messages twice.
|
|
248
|
+
# loggers.set_propagation(logger)
|
|
249
|
+
|
|
250
|
+
# Disabling the default logger in Python
|
|
251
|
+
logging.disable(logging.CRITICAL)
|
|
@@ -2,7 +2,6 @@ import os
|
|
|
2
2
|
from typing import Literal
|
|
3
3
|
|
|
4
4
|
from ... import filesystem, datetimes
|
|
5
|
-
from ...basics import list_of_dicts
|
|
6
5
|
from ...file_io import csvs
|
|
7
6
|
|
|
8
7
|
|
|
@@ -37,7 +36,7 @@ def get_logs(
|
|
|
37
36
|
if remove_logs and move_to_path:
|
|
38
37
|
raise ValueError('Both "remove_logs" and "move_to_path" cannot be True/specified at the same time.')
|
|
39
38
|
|
|
40
|
-
logs_files: list = filesystem.
|
|
39
|
+
logs_files: list = filesystem.get_file_paths_from_directory(
|
|
41
40
|
path, file_name_check_pattern=pattern,
|
|
42
41
|
add_last_modified_time=True, sort_by_last_modified_time=True)
|
|
43
42
|
|
|
@@ -47,12 +46,12 @@ def get_logs(
|
|
|
47
46
|
for single_file in logs_files:
|
|
48
47
|
if log_type == 'csv':
|
|
49
48
|
if header_type_of_files == 'all':
|
|
50
|
-
csv_content, _ = csvs.read_csv_to_list(single_file['
|
|
49
|
+
csv_content, _ = csvs.read_csv_to_list(single_file['file_path'], **print_kwargs)
|
|
51
50
|
logs_content.extend(csv_content)
|
|
52
51
|
elif header_type_of_files == 'first':
|
|
53
52
|
# The function gets empty header to read it from the CSV file, the returns the header that it read.
|
|
54
53
|
# Then each time the header is fed once again to the function.
|
|
55
|
-
csv_content, header = csvs.read_csv_to_list(single_file['
|
|
54
|
+
csv_content, header = csvs.read_csv_to_list(single_file['file_path'], header=header, **print_kwargs)
|
|
56
55
|
# Any way the first file will be read with header.
|
|
57
56
|
logs_content.extend(csv_content)
|
|
58
57
|
|
|
@@ -63,7 +62,7 @@ def get_logs(
|
|
|
63
62
|
if remove_logs:
|
|
64
63
|
# Remove the statistics files.
|
|
65
64
|
for single_file in logs_files:
|
|
66
|
-
filesystem.remove_file(single_file['
|
|
65
|
+
filesystem.remove_file(single_file['file_path'])
|
|
67
66
|
|
|
68
67
|
if move_to_path:
|
|
69
68
|
# Get formatted time stamp for file name.
|
|
@@ -76,8 +75,8 @@ def get_logs(
|
|
|
76
75
|
filesystem.create_directory(move_to_path_with_timestamp)
|
|
77
76
|
# Move the statistics files.
|
|
78
77
|
for single_file in logs_files:
|
|
79
|
-
single_file_name = filesystem.get_file_name(single_file['
|
|
78
|
+
single_file_name = filesystem.get_file_name(single_file['file_path'])
|
|
80
79
|
move_to_path_with_file = f'{move_to_path_with_timestamp}{os.sep}{single_file_name}'
|
|
81
|
-
filesystem.move_file(single_file['
|
|
80
|
+
filesystem.move_file(single_file['file_path'], move_to_path_with_file)
|
|
82
81
|
|
|
83
82
|
return logs_content
|
|
@@ -3,7 +3,7 @@ import threading
|
|
|
3
3
|
from .socket_client import SocketClient
|
|
4
4
|
from ..configparserw import ConfigParserWrapper
|
|
5
5
|
from ..loggingw import loggingw
|
|
6
|
-
from ...filesystem import
|
|
6
|
+
from ...filesystem import get_file_paths_from_directory
|
|
7
7
|
from ...file_io import jsons, file_io
|
|
8
8
|
|
|
9
9
|
|
|
@@ -57,14 +57,14 @@ def execute_test(config_static):
|
|
|
57
57
|
loggingw.get_logger_with_stream_handler("network")
|
|
58
58
|
|
|
59
59
|
# Get all the files in requests folder recursively.
|
|
60
|
-
request_file_list =
|
|
60
|
+
request_file_list = get_file_paths_from_directory(config['requests_directory'])
|
|
61
61
|
print(f"Found request files: {len(request_file_list)}")
|
|
62
62
|
|
|
63
63
|
# Get contents of all request files to list of contents.
|
|
64
64
|
requests_bytes_list: list = list()
|
|
65
65
|
for request_file_path in request_file_list:
|
|
66
66
|
if config['request_type'] == 'json':
|
|
67
|
-
request_file_content = jsons.read_json_file(request_file_path
|
|
67
|
+
request_file_content = jsons.read_json_file(request_file_path)
|
|
68
68
|
|
|
69
69
|
# If imported json is regular and not combined json.
|
|
70
70
|
if isinstance(request_file_content, dict):
|
|
@@ -79,13 +79,13 @@ def execute_test(config_static):
|
|
|
79
79
|
requests_bytes_list.extend(
|
|
80
80
|
get_key_values_from_json(json_dict, config['request_json_hex_key_list']))
|
|
81
81
|
elif config['request_type'] == 'string':
|
|
82
|
-
request_file_content = file_io.read_file(request_file_path
|
|
82
|
+
request_file_content = file_io.read_file(request_file_path)
|
|
83
83
|
# Convert string content to bytes and append to list.
|
|
84
84
|
requests_bytes_list.append(request_file_content.encode())
|
|
85
85
|
print(f"Extracted 1 request.")
|
|
86
86
|
elif config['request_type'] == 'binary':
|
|
87
87
|
# The content is already in bytes, so just appending.
|
|
88
|
-
requests_bytes_list.append(file_io.read_file(request_file_path
|
|
88
|
+
requests_bytes_list.append(file_io.read_file(request_file_path, 'rb'))
|
|
89
89
|
print(f"Extracted 1 request.")
|
|
90
90
|
|
|
91
91
|
print(f"Finished parsing. Parsed requests: {len(requests_bytes_list)}")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=eEODmu-TVbN7jQzj-QW2WqUEroxwfWlIzr-UPTPbi2E,122
|
|
2
2
|
atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
|
|
3
3
|
atomicshop/appointment_management.py,sha256=N3wVGJgrqJfsj_lqiRfaL3FxMEe57by5Stzanh189mk,7263
|
|
4
4
|
atomicshop/archiver.py,sha256=QuDKx6bJEM29u-7r2tsizXEEGJmPuv0Xm91JQOpg__8,7152
|
|
@@ -12,7 +12,7 @@ atomicshop/diff_check.py,sha256=RON9cSTgy3jAnwUmAUkOyfF6bgrBKOq9Sbgyl3RYodw,1235
|
|
|
12
12
|
atomicshop/dns.py,sha256=bNZOo5jVPzq7OT2qCPukXoK3zb1oOsyaelUwQEyK1SA,2500
|
|
13
13
|
atomicshop/domains.py,sha256=Rxu6JhhMqFZRcoFs69IoEd1PtYca0lMCG6F1AomP7z4,3197
|
|
14
14
|
atomicshop/emails.py,sha256=I0KyODQpIMEsNRi9YWSOL8EUPBiWyon3HRdIuSj3AEU,1410
|
|
15
|
-
atomicshop/filesystem.py,sha256=
|
|
15
|
+
atomicshop/filesystem.py,sha256=82osUMOas8DqUtR7mpcRWAqdx5JTRJ3e-o1VxetSXZg,30923
|
|
16
16
|
atomicshop/functions.py,sha256=VqLjxAxhaxUr-Ad8P1cw9bZGdZpbtqfCaXQyHf3CM9g,509
|
|
17
17
|
atomicshop/hashing.py,sha256=k_HXR7FnPUzLUKk8EiewJ_gLFBlWncZluiBwzplFMWs,3548
|
|
18
18
|
atomicshop/http_parse.py,sha256=nrf2rZcprLqtW8HVrV7TCZ1iTBcWRRy-mXIlAOzcaJs,9703
|
|
@@ -34,6 +34,7 @@ atomicshop/sound.py,sha256=KSzWRF8dkpEVXmFidIv-Eftc3kET-hQzQOxZRE7rMto,24297
|
|
|
34
34
|
atomicshop/speech_recognize.py,sha256=55-dIjgkpF93mvJnJuxSFuft5H5eRvGNlUj9BeIOZxk,5903
|
|
35
35
|
atomicshop/ssh_remote.py,sha256=Sas3nrQv8ardxR51t59xZZsYm8nvUcA7tMSqEDViRLk,17155
|
|
36
36
|
atomicshop/sys_functions.py,sha256=MTBxRve5bh58SPvhX3gMiGqHlSBuI_rdNN1NnnBBWqI,906
|
|
37
|
+
atomicshop/system_resources.py,sha256=CMJfhDv6dI9K3IejpyKu-6qRlfeTphaOCd2RaLxXDyg,1034
|
|
37
38
|
atomicshop/tempfiles.py,sha256=uq1ve2WlWehZ3NOTXJnpBBMt6HyCdBufqedF0HyzA6k,2517
|
|
38
39
|
atomicshop/timer.py,sha256=KxBBgVM8po6pUJDW8TgY1UXj0iiDmRmL5XDCq0VHAfU,1670
|
|
39
40
|
atomicshop/urls.py,sha256=CQl1j1kjEVDlAuYJqYD9XxPF1SUSgrmG8PjlcXNEKsQ,597
|
|
@@ -65,15 +66,15 @@ atomicshop/basics/bytes_arrays.py,sha256=M0o9OOx7TRcI2JQZSKpVLeBu5Q9jEaAwu7rE9_F
|
|
|
65
66
|
atomicshop/basics/classes.py,sha256=sgzsYpI8MhauNvfB_mmznROpyPbfgbqaneTTEWE7g9s,7336
|
|
66
67
|
atomicshop/basics/dicts.py,sha256=4DdkeVTzqr1Kh66vvIkzw4lz910SV0HMsEAGc7w5gug,8274
|
|
67
68
|
atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
|
|
68
|
-
atomicshop/basics/enums.py,sha256=
|
|
69
|
+
atomicshop/basics/enums.py,sha256=CeV8MfqWHihK7vvV6Mbzq7rD9JykeQfrJeFdLVzfHI4,3547
|
|
69
70
|
atomicshop/basics/exceptions.py,sha256=-1Gu8bHA3hrf11mmeaPuVE73jWV3EOyRgh2vSpWfveg,504
|
|
70
71
|
atomicshop/basics/guids.py,sha256=iRx5n18ATZWhpo748BwEjuLWLsu9y3OwF5-Adp-Dtik,403
|
|
71
72
|
atomicshop/basics/hexs.py,sha256=i8CTG-J0TGGa25yFSbWEvpVyHFnof_qSWUrmXY-ylKM,1054
|
|
72
73
|
atomicshop/basics/isinstancing.py,sha256=fQ35xfqbguQz2BUn-3a4KVGskhTcIn8JjRtxV2rFcRQ,876
|
|
73
74
|
atomicshop/basics/list_of_dicts.py,sha256=fu0H6dUD9uUo2kl7FYIrWzOQMwCmg7qRxGnFM5S319E,5716
|
|
74
75
|
atomicshop/basics/lists.py,sha256=ZyTjHyvkta-4_xCG1P-LFMENELmgAYlDdPq4hMRAOR8,2545
|
|
75
|
-
atomicshop/basics/multiprocesses.py,sha256=
|
|
76
|
-
atomicshop/basics/numbers.py,sha256=
|
|
76
|
+
atomicshop/basics/multiprocesses.py,sha256=4Bq5fzONuvCLfmwP11u9TKWGerTJv0zlwuHVKFhV6ms,13778
|
|
77
|
+
atomicshop/basics/numbers.py,sha256=II-jP8MG0IYvmjT9_BLEai65xzDJ0LTHK2eFVSrgVqo,527
|
|
77
78
|
atomicshop/basics/randoms.py,sha256=DmYLtnIhDK29tAQrGP1Nt-A-v8WC7WIEB8Edi-nk3N4,282
|
|
78
79
|
atomicshop/basics/strings.py,sha256=uhl-5cqZ3qLK7M38lOEDSGCAkG3xmeLoEU3V0oCgMxU,14358
|
|
79
80
|
atomicshop/basics/threads.py,sha256=xvgdDJdmgN0wmmARoZ-H7Kvl1GOcEbvgaeGL4M3Hcx8,2819
|
|
@@ -84,7 +85,7 @@ atomicshop/etw/dns_trace.py,sha256=ZbEf1gptnUnJwaRURUW9ENDwC9Q41Zl6NtxgMNELJcg,5
|
|
|
84
85
|
atomicshop/etw/etw.py,sha256=xVJNbfCq4KgRfsDnul6CrIdAMl9xRBixZ-hUyqiB2g4,2403
|
|
85
86
|
atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
86
87
|
atomicshop/file_io/csvs.py,sha256=4R4Kij8FmxNwXFjDtlF_A0flAk0Hj5nZKlEnqC5VxgQ,3125
|
|
87
|
-
atomicshop/file_io/docxs.py,sha256=
|
|
88
|
+
atomicshop/file_io/docxs.py,sha256=3IFtaurVhvntcuCL59bpFlzGyKxvDmVUjUQTHWluOMY,4369
|
|
88
89
|
atomicshop/file_io/file_io.py,sha256=s39zU5PRG-lqWFx6HNlZRLWMQJzpgIL0mkVyXdk8Jw4,7884
|
|
89
90
|
atomicshop/file_io/jsons.py,sha256=4xCnC6MfajLouXUFl2aVXUPvftQVf2eS5DgydPZHF_c,4170
|
|
90
91
|
atomicshop/file_io/tomls.py,sha256=oa0Wm8yMkPRXKN9jgBuTnKbioSOee4mABW5IMUFCYyU,3041
|
|
@@ -94,7 +95,7 @@ atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
94
95
|
atomicshop/mitm/connection_thread_worker.py,sha256=7f7T3t6ltndzb3125DgyRTzsD-oAbAfewDIhpNEeHRY,20630
|
|
95
96
|
atomicshop/mitm/import_config.py,sha256=BPZ62ozMUaUKn7Crt6dhzvUgD3dSKd17kMfbJNObvlI,7486
|
|
96
97
|
atomicshop/mitm/initialize_engines.py,sha256=qCtIjaR-sCbcliKKX_I65O6e4fqc-zDHELGglnL92cw,7687
|
|
97
|
-
atomicshop/mitm/initialize_mitm_server.py,sha256=
|
|
98
|
+
atomicshop/mitm/initialize_mitm_server.py,sha256=8Rwq4OzA4VKpzWNhxn8-CkCY_UdPIK64J4UrtD7oE3M,11230
|
|
98
99
|
atomicshop/mitm/message.py,sha256=u2U2f2SOHdBNU-6r1Ik2W14ai2EOwxUV4wVfGZA098k,1732
|
|
99
100
|
atomicshop/mitm/shared_functions.py,sha256=NeHABBlY-tmQRooWGVl2jZQx1wSTKJtEqG7mMvF2Jqo,4268
|
|
100
101
|
atomicshop/mitm/statistic_analyzer.py,sha256=1g5l6X-NbnHvh_TREJRumTDWgE4ixUNJ8pKGneKcf4Y,23524
|
|
@@ -162,8 +163,8 @@ atomicshop/wrappers/loggingw/checks.py,sha256=AGFsTsLxHQd1yAraa5popqLaGO9VM0KpcP
|
|
|
162
163
|
atomicshop/wrappers/loggingw/formatters.py,sha256=mUtcJJfmhLNrwUVYShXTmdu40dBaJu4TS8FiuTXI7ys,7189
|
|
163
164
|
atomicshop/wrappers/loggingw/handlers.py,sha256=qm5Fbu8eDmlstMduUe5nKUlJU5IazFkSnQizz8Qt2os,5479
|
|
164
165
|
atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1kvbhG-TDog8,2374
|
|
165
|
-
atomicshop/wrappers/loggingw/loggingw.py,sha256=
|
|
166
|
-
atomicshop/wrappers/loggingw/reading.py,sha256=
|
|
166
|
+
atomicshop/wrappers/loggingw/loggingw.py,sha256=v9WAseZXB50LluT9rIUcRvvevg2nLVKPgz3dbGejfV0,12151
|
|
167
|
+
atomicshop/wrappers/loggingw/reading.py,sha256=xs7L6Jo-vedrhCVP7m-cJo0VhWmoSoK86avR4Tm0kG4,3675
|
|
167
168
|
atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
168
169
|
atomicshop/wrappers/playwrightw/_tryouts.py,sha256=l1BLkFsiIMNlgv7nfZd1XGEvXQkIQkIcg48__9OaC00,4920
|
|
169
170
|
atomicshop/wrappers/playwrightw/base.py,sha256=zwHfbFZV7qqpmReJg5lEpKtOO23DRxEd61ZdoLBU_9Q,9833
|
|
@@ -188,12 +189,12 @@ atomicshop/wrappers/socketw/receiver.py,sha256=m8hXKOa8dqEQGUdcbYjshH8-j0CsMGRkg
|
|
|
188
189
|
atomicshop/wrappers/socketw/sender.py,sha256=hHpBLc0LCfOIUErq2mc0ATfp0tDDQ5XhcYT4hRAZARU,3680
|
|
189
190
|
atomicshop/wrappers/socketw/sni.py,sha256=GIm5uUJCh5i-pjY4FhSkoK4oo9uL_fFq1Mbr6PKXpBg,10014
|
|
190
191
|
atomicshop/wrappers/socketw/socket_client.py,sha256=X3Bo83EVZwDwrSIji7UOT9UGYutn-pfBTQcls_Jd7N8,20235
|
|
191
|
-
atomicshop/wrappers/socketw/socket_server_tester.py,sha256=
|
|
192
|
+
atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5eyTx0UGBOJi74viowtpU5Jvs,6291
|
|
192
193
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
193
194
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
194
195
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
|
|
195
|
-
atomicshop-2.
|
|
196
|
-
atomicshop-2.
|
|
197
|
-
atomicshop-2.
|
|
198
|
-
atomicshop-2.
|
|
199
|
-
atomicshop-2.
|
|
196
|
+
atomicshop-2.6.1.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
197
|
+
atomicshop-2.6.1.dist-info/METADATA,sha256=-WYBRbrqQqP3mICJe8ZuRix8y3SMBURo6A7wAD7Dbc4,10267
|
|
198
|
+
atomicshop-2.6.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
199
|
+
atomicshop-2.6.1.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
200
|
+
atomicshop-2.6.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|