atomicshop 2.5.16__py3-none-any.whl → 2.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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.5.16'
4
+ __version__ = '2.6.0'
@@ -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,312 @@ 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
+ while self.input_list:
93
+ system_resources.wait_for_resource_availability(
94
+ cpu_percent_max=self.cpu_percent_max,
95
+ memory_percent_max=self.memory_percent_max,
96
+ wait_time=self.wait_time)
97
+
98
+ # Apply the provided function to the items in the input list
99
+ results = pool.map(self.process_function, self.input_list)
100
+
101
+ # Flatten list of lists and update the queue
102
+ self.input_list = [item for sublist in results for item in sublist]
103
+
104
+
105
+ class _MultiProcessorTest:
106
+ def __init__(self, worker_count: int = 8, initialize_at_start: bool = True):
107
+ """
108
+ MultiProcessor class. Used to execute functions in parallel.
109
+ :param worker_count: integer, number of workers to execute functions in parallel.
110
+ :param initialize_at_start: boolean, if True, the workers will be initialized at the start of the class.
111
+
112
+ Usage:
113
+ # Example functions
114
+ def my_function1(x):
115
+ return x * x
116
+
117
+ def my_callback(result):
118
+ print(f"Result received: {result}")
119
+
120
+ # Usage example
121
+ if __name__ == "__main__":
122
+ processor = MultiProcessor()
123
+
124
+ # Adding tasks with callback (asynchronous)
125
+ for i in range(5):
126
+ processor.add_task(my_function1, i, callback=my_callback)
127
+
128
+ # Adding a task and waiting for the result (synchronous)
129
+ result = processor.add_task(my_function1, 10, wait=True)
130
+ print(f"Synchronous result: {result}")
131
+
132
+ # Shutdown and wait for all tasks to complete
133
+ processor.shutdown()
134
+ """
135
+
136
+ self.worker_count: int = worker_count
137
+ self.task_queue = multiprocessing.Queue()
138
+ self.result_queue = multiprocessing.Queue()
139
+ self.workers: list = list()
140
+
141
+ if initialize_at_start:
142
+ self._init_workers()
143
+
144
+ def _worker(self):
145
+ while True:
146
+ task = self.task_queue.get()
147
+ if task is None: # Termination signal
148
+ break
149
+ func, args, kwargs, callback, return_result = task
150
+ try:
151
+ result = func(*args, **kwargs)
152
+ if callback:
153
+ callback(result)
154
+ if return_result:
155
+ self.result_queue.put(result)
156
+ except Exception as e:
157
+ if callback:
158
+ callback(e)
159
+ if return_result:
160
+ self.result_queue.put(e)
161
+
162
+ def _init_workers(self):
163
+ for _ in range(self.worker_count):
164
+ p = multiprocessing.Process(target=self._worker)
165
+ p.start()
166
+ self.workers.append(p)
167
+
168
+ def add_task(self, func, *args, callback=None, wait=False, **kwargs):
169
+ """
170
+ Add a task to the queue.
171
+ :param func: reference, function to execute.
172
+ :param args: arguments for the function.
173
+ :param callback: reference, function to execute after the task is completed. The result of the task will be
174
+ passed to the callback function.
175
+ :param wait: boolean, if True, the function will wait for the task to complete and return the result.
176
+ :param kwargs: keyword arguments for the function.
177
+ :return:
178
+ """
179
+ if wait:
180
+ self.task_queue.put((func, args, kwargs, None, True))
181
+ return self.result_queue.get()
182
+ else:
183
+ self.task_queue.put((func, args, kwargs, callback, False))
184
+ return None
185
+
186
+ def shutdown(self):
187
+ # Signal workers to exit
188
+ for _ in self.workers:
189
+ self.task_queue.put(None)
190
+
191
+ # Wait for all workers to finish
192
+ for p in self.workers:
193
+ p.join()
194
+
195
+
196
+ class _MultiConcuratorTest:
197
+ def __init__(self, max_workers: int = 2, initialize_at_start: bool = False):
198
+ """
199
+ MultiConcurator class. Used to execute functions in parallel with the ProcessPoolExecutor.
200
+
201
+ :param max_workers: integer, number of workers to execute functions in parallel.
202
+ :param initialize_at_start: boolean, if True, the workers will be initialized at the start of the class.
203
+
204
+ Usage 1:
205
+ if __name__ == "__main__":
206
+ concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
207
+
208
+ # ... add tasks to concurator ...
209
+
210
+ concurator.wait_for_completion()
211
+
212
+ # Continuously get and process results
213
+ for result in concurator.get_results():
214
+ print(result)
215
+
216
+ concurator.shutdown()
217
+
218
+ Usage 2:
219
+ # Basic Usage - Executing Functions in Parallel
220
+ This example shows how to execute a simple function in parallel.
221
+
222
+ def square(number):
223
+ return number * number
224
+
225
+ if __name__ == "__main__":
226
+ concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
227
+
228
+ # Submit tasks
229
+ for num in range(10):
230
+ concurator.add_to_queue(square, num)
231
+
232
+ # Wait for completion and handle results
233
+ concurator.wait_for_completion()
234
+
235
+ # Retrieve results
236
+ while not concurator.queue.empty():
237
+ result = concurator.queue.get()
238
+ print(result)
239
+
240
+ concurator.shutdown()
241
+
242
+ Usage 3:
243
+ # Handling Results with a Callback Function
244
+ # This example demonstrates handling the results of each task using a callback function.
245
+
246
+ def multiply(x, y):
247
+ return x * y
248
+
249
+ def handle_result(result):
250
+ print(f"Result: {result}")
251
+
252
+ if __name__ == "__main__":
253
+ concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
254
+
255
+ for i in range(5):
256
+ concurator.add_to_queue(multiply, i, i+1)
257
+
258
+ concurator.wait_for_completion(handle_result)
259
+ concurator.shutdown()
260
+
261
+ Usage 4:
262
+ # Dynamically Initializing the Executor
263
+ # This example shows how to initialize the executor dynamically and process tasks.
264
+
265
+ def compute_power(base, exponent):
266
+ return base ** exponent
267
+
268
+ if __name__ == "__main__":
269
+ concurator = MultiConcurator(max_workers=3)
270
+
271
+ # Initialize executor when needed
272
+ concurator.init_executor()
273
+
274
+ for i in range(1, 5):
275
+ concurator.add_to_queue(compute_power, i, 2)
276
+
277
+ concurator.wait_for_completion()
278
+ while not concurator.queue.empty():
279
+ print(concurator.queue.get())
280
+
281
+ concurator.shutdown()
282
+
283
+ Usage 5:
284
+ # Handling Exceptions in Tasks
285
+ # This example demonstrates how you can handle exceptions that might occur in the tasks.
286
+
287
+ def risky_division(x, y):
288
+ if y == 0:
289
+ raise ValueError("Cannot divide by zero")
290
+ return x / y
291
+
292
+ def handle_result(result):
293
+ if isinstance(result, Exception):
294
+ print(f"Error occurred: {result}")
295
+ else:
296
+ print(f"Result: {result}")
297
+
298
+ if __name__ == "__main__":
299
+ concurator = MultiConcurator(max_workers=4, initialize_at_start=True)
300
+
301
+ concurator.add_to_queue(risky_division, 10, 2)
302
+ concurator.add_to_queue(risky_division, 10, 0) # This will raise an exception
303
+
304
+ concurator.wait_for_completion(handle_result)
305
+ concurator.shutdown()
306
+ """
307
+
308
+ self.executor = None
309
+ self.max_workers: int = max_workers
310
+ self.futures: list = list()
311
+ self.queue = multiprocessing.Queue()
312
+
313
+ if initialize_at_start:
314
+ self.init_executor()
315
+
316
+ def init_executor(self):
317
+ if self.executor is None:
318
+ self.executor = ProcessPoolExecutor(max_workers=self.max_workers)
319
+
320
+ def add_to_queue(self, func, *args, **kwargs):
321
+ if self.executor is None:
322
+ raise RuntimeError("Executor has not been initialized. Call init_executor() first.")
323
+ future = self.executor.submit(func, *args, **kwargs)
324
+ self.futures.append(future)
325
+ return future
326
+
327
+ def wait_for_completion(self, handle_result=None):
328
+ for future in as_completed(self.futures):
329
+ result = future.result()
330
+ if handle_result:
331
+ handle_result(result)
332
+ else:
333
+ self.queue.put(result)
334
+
335
+ def get_results(self):
336
+ while not self.queue.empty():
337
+ yield self.queue.get()
338
+
339
+ def shutdown(self):
340
+ if self.executor is not None:
341
+ self.executor.shutdown()
342
+ self.executor = None
@@ -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)
@@ -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 or 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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.5.16
3
+ Version: 2.6.0
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=ayLlabN9C1kOn1T8udiljMqhA2XOEFO5ihTvhBJ00kA,123
1
+ atomicshop/__init__.py,sha256=NXDiuw7KfoCVJPfpRXHeFAgfmcvLx-4zWrrJZldKjJ0,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
@@ -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=rYRNsl9BaJgFSSgygXNtUsVFxUb9WGxZ1Ek7bKLomVo,1033
37
38
  atomicshop/tempfiles.py,sha256=uq1ve2WlWehZ3NOTXJnpBBMt6HyCdBufqedF0HyzA6k,2517
38
39
  atomicshop/timer.py,sha256=KxBBgVM8po6pUJDW8TgY1UXj0iiDmRmL5XDCq0VHAfU,1670
39
40
  atomicshop/urls.py,sha256=CQl1j1kjEVDlAuYJqYD9XxPF1SUSgrmG8PjlcXNEKsQ,597
@@ -72,8 +73,8 @@ atomicshop/basics/hexs.py,sha256=i8CTG-J0TGGa25yFSbWEvpVyHFnof_qSWUrmXY-ylKM,105
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=GVeyF-r9Q6SAjpJ46eqkZQ4QUli554n-CAIE_W8pij8,958
76
- atomicshop/basics/numbers.py,sha256=FRjAH1Thk3z3ayxq0Ik6Wh93ELDRk1geyDYTv8amZBE,165
76
+ atomicshop/basics/multiprocesses.py,sha256=On2PbXDpVBkhdocISNDTGFcElpSOONfYjkIPKIRCIHI,12887
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
@@ -162,7 +163,7 @@ 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=30uaJOJhPJExYptEmwc5HWScI2RbnDlNgybOsxW7klc,11609
166
+ atomicshop/wrappers/loggingw/loggingw.py,sha256=v9WAseZXB50LluT9rIUcRvvevg2nLVKPgz3dbGejfV0,12151
166
167
  atomicshop/wrappers/loggingw/reading.py,sha256=cHco0EG8bEB6fJ7JxkFdV_d8J-voYRBCUzS-qVqv4aU,3697
167
168
  atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
168
169
  atomicshop/wrappers/playwrightw/_tryouts.py,sha256=l1BLkFsiIMNlgv7nfZd1XGEvXQkIQkIcg48__9OaC00,4920
@@ -192,8 +193,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=VfNthyBvgI5tL9v3Qprh4
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.5.16.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
196
- atomicshop-2.5.16.dist-info/METADATA,sha256=02N26vDhqG2L4xhGjWCiug9P7OXAs6WlvUbzN9q6h7k,10268
197
- atomicshop-2.5.16.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
198
- atomicshop-2.5.16.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
199
- atomicshop-2.5.16.dist-info/RECORD,,
196
+ atomicshop-2.6.0.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
197
+ atomicshop-2.6.0.dist-info/METADATA,sha256=hIocyrjd0H35Wh9NFhQ_OldHC9DtqBZfV2LVpZ7IQdo,10267
198
+ atomicshop-2.6.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
199
+ atomicshop-2.6.0.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
200
+ atomicshop-2.6.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.3)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5