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 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.15.6'
4
+ __version__ = '2.15.8'
@@ -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 match_pattern_against_string(
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(file_path: str, backup_directory: str, timestamp_as_prefix: bool = False) -> None:
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 backup.
1420
- :param backup_directory: The directory to backup the file 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 match_pattern_against_running_processes_cmdlines(
255
+ def get_running_processes_by_cmdline_pattern(
251
256
  pattern: str,
252
- process_name_case_insensitive: bool = False,
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 process_name_case_insensitive: boolean, if True, the process name will be matched case insensitive.
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='pywin32', connect_on_init=True)
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'] and \
277
- strings.match_pattern_against_string(
278
- pattern, process['cmdline'], case_insensitive=process_name_case_insensitive,
279
- prefix_suffix=prefix_suffix):
280
- matched_cmdlines.append(process['cmdline'])
281
- # If 'first' was set to 'True' we will stop, since we found the first match.
282
- if first:
283
- break
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
@@ -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.match_pattern_against_running_processes_cmdlines(
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.15.6
3
+ Version: 2.15.8
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=1hdUGTA9S4fKop2fWaz_-LGGl280RKNToC4zKa_Ed7E,123
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=GIuRNtqUkUXrqKhvRxC9rRulK8FpgKqPcPXG1bf1fac,55030
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=U2gyRl0bw2138y-rOMABMVptRvAL81ZfX1JyfxJI_Oo,15973
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=T4MpEpwxqsiOSnXcwYkqMKB5okHiJfvUCO7t5kcRtBg,18316
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=impveU8MhbD2oizBoGLuj00wuyB8l_EiRRGIESdFESs,5359
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=XVqbr7btnT1aJioCFOBnudKGZ5-L9sTpBFWb4DHn2Rw,847
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.6.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
298
- atomicshop-2.15.6.dist-info/METADATA,sha256=Q5MZL59GkPTmEyDcTAlcDfRmFrOs9N8yQt6r-xnCKAM,10502
299
- atomicshop-2.15.6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
300
- atomicshop-2.15.6.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
301
- atomicshop-2.15.6.dist-info/RECORD,,
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,,