atomicshop 2.19.12__py3-none-any.whl → 2.19.14__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.19.12'
4
+ __version__ = '2.19.14'
@@ -10,6 +10,8 @@ from ..file_io.file_io import read_file
10
10
 
11
11
 
12
12
  """
13
+ Using logger in the class only once during the import of the module.
14
+
13
15
  class ParserParent:
14
16
  # Initializing the logger in the "class variable" section will leave the instance of the logger initiated
15
17
  # and the rest of the instances of the class will use the same logger.
@@ -28,6 +30,58 @@ class ParserParent:
28
30
  """
29
31
 
30
32
 
33
+ """
34
+ Using Base class for easier interfacing on subclasses.
35
+
36
+ recognition/recognition_base.py:
37
+ from abc import abstractmethod
38
+
39
+
40
+ class Recognizer:
41
+ @abstractmethod
42
+ def recognize_vendor(self, file_path: str) -> str:
43
+ pass
44
+
45
+ @abstractmethod
46
+ def recognize_family(self, bytes_list: list[str]]) -> str:
47
+ pass
48
+
49
+
50
+ recognition/super_vendor.py:
51
+ from .recognition_base import Recognizer
52
+
53
+ class SupervendorRecognizer(Recognizer):
54
+ def recognize_vendor(self, file_path: str) -> str:
55
+ classification_string: str = <Some logic to classify the SuperVendor>
56
+ return classification_string
57
+
58
+ def recognize_family(self, bytes_list: list[str]]) -> str:
59
+ family_classification_string: str = <Some logic to classify the family of the SuperVendor>
60
+ return family_classification_string
61
+
62
+
63
+ main_script.py:
64
+ from . import recognition
65
+ from .recognition.recognition_base import Recognizer
66
+
67
+ # Get the list of all the recognizers in the recognition package.
68
+ recognizers_list: list = classes.get_list_of_classes_in_module(
69
+ imported_package=recognition, imported_base_class=Recognizer)
70
+
71
+ # Get the list of all the vendors from the file.
72
+ vendors_list: list = list()
73
+ for recognizer in recognizers_list:
74
+ recognizer_instance = recognizer()
75
+ vendor_name: str = recognizer_instance.recognize_vendor(file_object=file_path)
76
+ if vendor_name:
77
+ vendors_list.append((vendor_name, recognizer_instance))
78
+
79
+ # Get the families of the vendors.
80
+ for vendor_name, recognizer_instance in vendors_list:
81
+ family_name: str = recognizer_instance.recognize_family(bytes_list=file_bytes_list)
82
+ print(f"Vendor: {vendor_name}, Family: {family_name}")
83
+ """
84
+
31
85
  def get_list_of_classes_in_module(
32
86
  imported_package,
33
87
  imported_base_class
@@ -66,6 +120,22 @@ def get_list_of_classes_in_module(
66
120
  # Get the list of classes
67
121
  unpacker_classes = get_list_of_classes_in_module(imported_package=unpackers, imported_base_class=Unpacker)
68
122
 
123
+ # Initialize the classes
124
+ for unpacker_class in unpacker_classes:
125
+ unpacker_instance = unpacker_class()
126
+ unpacker_instance.unpack("file_path")
127
+ ----------------------------
128
+ # You can also initialize the list of classes dynamically and after that execute methods.
129
+ # Example:
130
+ unpacker_classes = get_list_of_classes_in_module(imported_package=unpackers, imported_base_class=Unpacker)
131
+
132
+ instance_list: list = []
133
+ for unpacker_class in unpacker_classes:
134
+ instance_list.append(unpacker_class())
135
+
136
+ for instance in instance_list:
137
+ instance.unpack("file_path")
138
+
69
139
  :param imported_package:
70
140
  :param imported_base_class:
71
141
  :return:
@@ -2,9 +2,11 @@ import threading
2
2
  from pathlib import Path
3
3
  import time
4
4
  import multiprocessing.managers
5
+ import queue
6
+
5
7
 
6
8
  from ..wrappers.pywin32w.win_event_log.subscribes import process_create, process_terminate
7
- from .. import get_process_list, get_process_name_cmd_dll
9
+ from .. import get_process_list
8
10
  from ..print_api import print_api
9
11
 
10
12
 
@@ -15,6 +17,10 @@ WAIT_FOR_PROCESS_POLLER_PID_SECONDS: int = 3
15
17
  WAIT_FOR_PROCESS_POLLER_PID_COUNTS: int = WAIT_FOR_PROCESS_POLLER_PID_SECONDS * 10
16
18
 
17
19
 
20
+ class PidProcessConverterPIDNotFoundError(Exception):
21
+ pass
22
+
23
+
18
24
  class SimpleProcessPool:
19
25
  """
20
26
  THE POOL OF PROCESSES IS NOT REAL TIME!!!
@@ -106,14 +112,16 @@ class SimpleProcessPool:
106
112
 
107
113
  self._processes: dict = dict()
108
114
  self._running: bool = False
115
+ self.shared_dict_update_queue: queue.Queue = queue.Queue()
116
+ self.empty_cmdline_queue: queue.Queue = queue.Queue()
109
117
 
110
118
  def start(self):
111
119
  self._running = True
112
120
 
113
121
  self._processes = get_process_list.GetProcessList(
114
- get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
122
+ get_method='psutil', connect_on_init=True).get_processes(as_dict=True)
115
123
 
116
- thread_get_queue = threading.Thread(target=self._start_main_thread, args=(self.process_pool_shared_dict_proxy,))
124
+ thread_get_queue = threading.Thread(target=self._start_main_thread, args=(self.shared_dict_update_queue,))
117
125
  thread_get_queue.daemon = True
118
126
  thread_get_queue.start()
119
127
 
@@ -121,13 +129,21 @@ class SimpleProcessPool:
121
129
  thread_process_termination.daemon = True
122
130
  thread_process_termination.start()
123
131
 
132
+ thread_get_psutil_commandline = threading.Thread(target=self._thread_get_psutil_commandline)
133
+ thread_get_psutil_commandline.daemon = True
134
+ thread_get_psutil_commandline.start()
135
+
136
+ thread_update_shared_dict = threading.Thread(target=self._update_shared_dict, args=(self.shared_dict_update_queue,))
137
+ thread_update_shared_dict.daemon = True
138
+ thread_update_shared_dict.start()
139
+
124
140
  def stop(self):
125
141
  self._running = False
126
142
 
127
143
  def get_processes(self):
128
144
  return self._processes
129
145
 
130
- def _start_main_thread(self, process_pool_shared_dict_proxy):
146
+ def _start_main_thread(self, shared_dict_update_queue):
131
147
  get_instance = process_create.ProcessCreateSubscriber()
132
148
  get_instance.start()
133
149
 
@@ -137,17 +153,35 @@ class SimpleProcessPool:
137
153
  process_name = Path(event['NewProcessName']).name
138
154
  command_line = event['CommandLine']
139
155
 
156
+ # The event log tracing method doesn't always give the command line, unlike the psutil method.
157
+ # So, we'll get the command line from the current psutil snapshot separately.
158
+ if command_line == '':
159
+ self.empty_cmdline_queue.put(process_id)
160
+
140
161
  self._processes[process_id] = {
141
162
  'name': process_name,
142
163
  'cmdline': command_line
143
164
  }
144
165
 
145
166
  # Update the multiprocessing shared dict proxy.
146
- if process_pool_shared_dict_proxy is not None:
147
- process_pool_shared_dict_proxy.clear()
148
- process_pool_shared_dict_proxy.update(self._processes)
167
+ shared_dict_update_queue.put(dict(self._processes))
149
168
 
150
- # print_api(f'Process [{process_id}] added to the pool.', color='blue')
169
+ def _thread_get_psutil_commandline(self):
170
+ """
171
+ This function will get an entry from the queue where command line is missing and get the command line
172
+ from the psutil snapshot.
173
+ """
174
+
175
+ while self._running:
176
+ empty_cmd_pid = self.empty_cmdline_queue.get()
177
+ current_psutil_process_snapshot: dict = get_process_list.GetProcessList(
178
+ get_method='psutil', connect_on_init=True).get_processes(as_dict=True)
179
+ command_line = current_psutil_process_snapshot[empty_cmd_pid]['cmdline']
180
+
181
+ self._processes[empty_cmd_pid]['cmdline'] = command_line
182
+
183
+ # Update the multiprocessing shared dict proxy.
184
+ self.shared_dict_update_queue.put(dict(self._processes))
151
185
 
152
186
  def _thread_process_termination(self):
153
187
  process_terminate_instance = process_terminate.ProcessTerminateSubscriber()
@@ -188,6 +222,15 @@ class SimpleProcessPool:
188
222
  _ = self._processes.pop(process_id, None)
189
223
  # print_api(f'Process [{process_id}] removed from the pool.', color='yellow')
190
224
 
225
+ self.shared_dict_update_queue.put(dict(self._processes))
226
+
227
+ def _update_shared_dict(self, shared_dict_update_queue):
228
+ while self._running:
229
+ current_process_pool = shared_dict_update_queue.get()
230
+ if self.process_pool_shared_dict_proxy is not None:
231
+ self.process_pool_shared_dict_proxy.clear()
232
+ self.process_pool_shared_dict_proxy.update(current_process_pool)
233
+
191
234
 
192
235
  class PidProcessConverter:
193
236
  """
@@ -204,7 +247,7 @@ class PidProcessConverter:
204
247
 
205
248
  self.process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy = process_pool_shared_dict_proxy
206
249
 
207
- self.get_process_with_dll_instance = get_process_name_cmd_dll.ProcessNameCmdline(load_dll=True)
250
+ self.get_process_with_psutil = get_process_list.GetProcessList(get_method='psutil', connect_on_init=True)
208
251
 
209
252
  def get_process_by_pid(self, pid: int):
210
253
  """
@@ -219,18 +262,20 @@ class PidProcessConverter:
219
262
  counter = 0
220
263
  process_dict: dict = dict()
221
264
  while counter < WAIT_FOR_PROCESS_POLLER_PID_COUNTS:
222
- if pid not in self.process_pool_shared_dict_proxy:
265
+ # We need it so that the pool will not change in the middle of the process.
266
+ current_pid_pool = convert_proxy_dict_to_dict(self.process_pool_shared_dict_proxy)
267
+ if pid not in current_pid_pool:
223
268
  # print(dict(self.process_pool_shared_dict_proxy))
224
269
  time.sleep(0.1)
225
270
  counter += 1
226
271
  else:
227
- process_dict = self.process_pool_shared_dict_proxy[pid]
272
+ process_dict = current_pid_pool[pid]
228
273
  break
229
274
 
230
- if counter == WAIT_FOR_PROCESS_POLLER_PID_COUNTS and not process_dict:
231
- print_api(f"Error: The PID [{pid}] is not in the pool, trying DLL snapshot.", color='yellow')
275
+ if not process_dict:
276
+ print_api(f"Error: The PID [{pid}] is not in the pool, trying psutil snapshot.", color='yellow')
232
277
  # Last resort, try to get the process name by current process snapshot.
233
- processes = self.get_process_with_dll_instance.get_process_details(as_dict=True)
278
+ processes = self.get_process_with_psutil.get_processes(as_dict=True)
234
279
  if pid not in processes:
235
280
  print_api(f"Error: Couldn't get the process name for PID: {pid}.", color='red')
236
281
  process_dict = {
@@ -242,3 +287,25 @@ class PidProcessConverter:
242
287
 
243
288
  return process_dict
244
289
 
290
+
291
+ def convert_proxy_dict_to_dict(proxy_dict: multiprocessing.managers.DictProxy) -> dict:
292
+ """
293
+ Convert the multiprocessing shared dict proxy to a normal dict.
294
+
295
+ :param proxy_dict: multiprocessing.managers.DictProxy, the shared dict proxy.
296
+ :return: dict, the normal dict.
297
+ """
298
+
299
+ # Create a snapshot of the keys
300
+ keys = list(proxy_dict.keys())
301
+ current_pid_pool = {}
302
+
303
+ for key in keys:
304
+ try:
305
+ # Attempt to retrieve the value for each key
306
+ current_pid_pool[key] = proxy_dict[key]
307
+ except KeyError:
308
+ # The key was removed concurrently; skip it
309
+ continue
310
+
311
+ return current_pid_pool
@@ -207,7 +207,9 @@ def _fetch_content(
207
207
  'playwright_text',
208
208
  'js_text',
209
209
  'playwright_html',
210
+ 'playwright_html_to_text',
210
211
  'js_html',
212
+ 'js_html_to_text',
211
213
  'playwright_copypaste'
212
214
  ],
213
215
  headless: bool = True):
@@ -260,18 +262,12 @@ def _fetch_content(
260
262
  elif text_fetch_method == "js_text":
261
263
  # Use JavaScript to extract only the visible text from the page
262
264
  text_content: str = page.evaluate("document.body.innerText")
263
- elif text_fetch_method == "playwright_html":
265
+ elif "playwright_html" in text_fetch_method:
264
266
  # Get the full HTML content of the page
265
- html = page.content()
266
- # Parse the HTML using BeautifulSoup and extract the text
267
- soup = BeautifulSoup(html, 'html.parser')
268
- text_content = soup.get_text()
269
- elif text_fetch_method == "js_html":
267
+ text_content = page.content()
268
+ elif "js_html" in text_fetch_method:
270
269
  # Use JavaScript to extract the full text from the page
271
- html = page.evaluate('document.documentElement.outerHTML')
272
- # Parse the HTML using BeautifulSoup and extract the text
273
- soup = BeautifulSoup(html, 'html.parser')
274
- text_content = soup.get_text()
270
+ text_content = page.evaluate('document.documentElement.outerHTML')
275
271
  elif text_fetch_method == "playwright_copypaste":
276
272
  # Focus the page and simulate Ctrl+A and Ctrl+C
277
273
  page.keyboard.press("Control+a") # Select all text
@@ -281,6 +277,11 @@ def _fetch_content(
281
277
  else:
282
278
  raise ValueError(f"Invalid text_fetch_method: {text_fetch_method}")
283
279
 
280
+ if "to_text" in text_fetch_method:
281
+ # Convert HTML to plain text using BeautifulSoup
282
+ soup = BeautifulSoup(text_content, "html.parser")
283
+ text_content = soup.get_text()
284
+
284
285
  # text = page.evaluate('document.body.textContent')
285
286
  # text = page.eval_on_selector('body', 'element => element.innerText')
286
287
  # text = page.eval_on_selector('body', 'element => element.textContent')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.19.12
3
+ Version: 2.19.14
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=XtGdtCQBXBDdG8_6wWA54tuzPJ5IE_hCw_e_N2FoXLM,124
1
+ atomicshop/__init__.py,sha256=jnzkSiVKr7rNaRPe51te8qpwDBNj2V37gzYMQVqPkoQ,124
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
@@ -92,7 +92,7 @@ atomicshop/basics/ansi_escape_codes.py,sha256=uGVRW01v2O052701sOfAdCaM_GLF_cu_jr
92
92
  atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
93
93
  atomicshop/basics/booleans.py,sha256=V36NaMf8AffhCom_ovQeOZlYcdtGyIcQwWKki6h7O0M,1745
94
94
  atomicshop/basics/bytes_arrays.py,sha256=xfFW9CBQyzf0iXNWpx-EfrxtN3-tiKzuczPzOb6cOdU,7190
95
- atomicshop/basics/classes.py,sha256=UayCzPs3eynI3wOzSu-2IJSmTwOB4HwPwgVI2F-7_lQ,12648
95
+ atomicshop/basics/classes.py,sha256=dBSQktjc1GmphpD_qOGYNDl-mQcmwg8OkhSqtXPfHEU,15301
96
96
  atomicshop/basics/dicts.py,sha256=DeYHIh940pMMBrFhpXt4dsigFVYzTrlqWymNo4Pq_Js,14049
97
97
  atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
98
98
  atomicshop/basics/enumerations.py,sha256=41VVQYh_vnVapggxKg2IRU5e_EiMpZzX1n1mtxvoSzM,1364
@@ -170,7 +170,7 @@ atomicshop/permissions/ubuntu_permissions.py,sha256=n8z1vcIXDts4zLVue33dtJiTopjg
170
170
  atomicshop/permissions/win_permissions.py,sha256=eDQm1jfK9x_hkbLqIJjFTwfqinAWQ0iSr0kW3XrF1BE,1272
171
171
  atomicshop/process_poller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
172
  atomicshop/process_poller/process_pool.py,sha256=4Qs427qd7OcBxu5PMFU5PTmyuxRy0vgj2GLsRt0IoEw,9565
173
- atomicshop/process_poller/simple_process_pool.py,sha256=E0w66imLQQ0phHuTQfTaApOTurX0duE9vw1trjUY7rI,9894
173
+ atomicshop/process_poller/simple_process_pool.py,sha256=ap9nCywHZ-eta7EHe0mTYS45-zYvMjgvfESXAuG0Wi4,12578
174
174
  atomicshop/process_poller/tracer_base.py,sha256=IOiHcnmF-MccOSCErixN5mve9RifZ9cPnGVHCIRchrs,1091
175
175
  atomicshop/process_poller/pollers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
176
176
  atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py,sha256=XRRfOIy62iOYU8IKRcyECWiL0rqQ35DeYbPsv_SHDVM,4510
@@ -284,7 +284,7 @@ atomicshop/wrappers/playwrightw/javascript.py,sha256=_bW7CAtm0Y8IHYrAalg5HpPFnk6
284
284
  atomicshop/wrappers/playwrightw/keyboard.py,sha256=zN3YddGO-qUkn6C0CRVFejP4cTuaUwXLDNFhFREjERY,422
285
285
  atomicshop/wrappers/playwrightw/locators.py,sha256=6wsLywZxDuii7mwv-zQsRbqQC8r7j96Bma5b5_7ZoVo,2411
286
286
  atomicshop/wrappers/playwrightw/mouse.py,sha256=-2FZbQtjgH7tdXWld6ZPGqlKFUdf5in0ujN0hewxa50,656
287
- atomicshop/wrappers/playwrightw/scenarios.py,sha256=Xvl1jUmQhd4l0MmOUgQKfgGleblIyE-qC3wuoyx16tU,11531
287
+ atomicshop/wrappers/playwrightw/scenarios.py,sha256=oLrZiWnIc09I0Zvh3IxPVDPsQ-Kbr4QM0TeyXGvlsaY,11494
288
288
  atomicshop/wrappers/playwrightw/waits.py,sha256=PBFdz_PoM7Fo7O8hLqMrxNPzBEYgPoXwZceFFCGGeu8,7182
289
289
  atomicshop/wrappers/psutilw/cpus.py,sha256=w6LPBMINqS-T_X8vzdYkLS2Wzuve28Ydp_GafTCngrc,236
290
290
  atomicshop/wrappers/psutilw/disks.py,sha256=3ZSVoommKH1TWo37j_83frB-NqXF4Nf5q5mBCX8G4jE,9221
@@ -329,8 +329,8 @@ atomicshop/wrappers/socketw/statistics_csv.py,sha256=fgMzDXI0cybwUEqAxprRmY3lqbh
329
329
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
330
330
  atomicshop/wrappers/winregw/winreg_installed_software.py,sha256=Qzmyktvob1qp6Tjk2DjLfAqr_yXV0sgWzdMW_9kwNjY,2345
331
331
  atomicshop/wrappers/winregw/winreg_network.py,sha256=AENV88H1qDidrcpyM9OwEZxX5svfi-Jb4N6FkS1xtqA,8851
332
- atomicshop-2.19.12.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
333
- atomicshop-2.19.12.dist-info/METADATA,sha256=IjBkWX6cc09byHYWcjX8hdW_eEmXddbKu1h0hNYUybM,10631
334
- atomicshop-2.19.12.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
335
- atomicshop-2.19.12.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
336
- atomicshop-2.19.12.dist-info/RECORD,,
332
+ atomicshop-2.19.14.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
333
+ atomicshop-2.19.14.dist-info/METADATA,sha256=1ZQp-Wt5osHkFGnEavFkPEC41-jmfqJsSsktj0T7aBk,10631
334
+ atomicshop-2.19.14.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
335
+ atomicshop-2.19.14.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
336
+ atomicshop-2.19.14.dist-info/RECORD,,