atomicshop 2.15.11__py3-none-any.whl → 3.10.5__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.
Files changed (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,9 @@
1
1
  import threading
2
2
  from pathlib import Path
3
3
  import time
4
+ import multiprocessing.managers
5
+ import queue
6
+
4
7
 
5
8
  from ..wrappers.pywin32w.win_event_log.subscribes import process_create, process_terminate
6
9
  from .. import get_process_list
@@ -10,6 +13,13 @@ from ..print_api import print_api
10
13
  WAIT_BEFORE_PROCESS_TERMINATION_CHECK_SECONDS: float = 3
11
14
  WAIT_BEFORE_PROCESS_TERMINATION_CHECK_COUNTS: float = WAIT_BEFORE_PROCESS_TERMINATION_CHECK_SECONDS * 10
12
15
 
16
+ WAIT_FOR_PROCESS_POLLER_PID_SECONDS: int = 3
17
+ WAIT_FOR_PROCESS_POLLER_PID_COUNTS: int = WAIT_FOR_PROCESS_POLLER_PID_SECONDS * 10
18
+
19
+
20
+ class PidProcessConverterPIDNotFoundError(Exception):
21
+ pass
22
+
13
23
 
14
24
  class SimpleProcessPool:
15
25
  """
@@ -19,29 +29,99 @@ class SimpleProcessPool:
19
29
  The idea is similar to the process_poller.process_pool.ProcessPool class, but this class is simpler and uses
20
30
  only the pywin32 tracing of the Windows Event Log Process Creation and Process Termination events.
21
31
  The simple process pool is used to get things simpler than the process_pool.ProcessPool class.
32
+
33
+ Example of starting the process pool in multiprocess:
34
+ import sys
35
+
36
+ from atomicshop.process_poller import simple_process_pool
37
+
38
+
39
+ def start_process_pool(process_pool_shared_dict_proxy):
40
+ process_poller = simple_process_pool.SimpleProcessPool(
41
+ process_pool_shared_dict_proxy=process_pool_shared_dict_proxy)
42
+ process_poller.start()
43
+
44
+ try:
45
+ # Keep the process alive.
46
+ while True:
47
+ time.sleep(1)
48
+ except KeyboardInterrupt:
49
+ process_poller.stop()
50
+
51
+
52
+ def main():
53
+ # Create the shared multiprocessing dictionary of the process pool.
54
+ manager = multiprocessing.Manager()
55
+ multiprocess_dict_proxy = manager.dict()
56
+
57
+ # Start the process pool in a separate process.
58
+ pool_process = multiprocessing.Process(target=start_process_pool, args=(multiprocess_dict_proxy,))
59
+ pool_process.start()
60
+
61
+ # Pass the shared dict proxy to other functions.
62
+
63
+
64
+ if __name__ == '__main__':
65
+ sys.exit(main())
22
66
  """
23
67
 
24
68
  def __init__(
25
69
  self,
26
- wait_before_pid_remove_seconds: float = 5
70
+ wait_before_pid_remove_seconds: float = 5,
71
+ process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy = None
27
72
  ):
28
73
  """
29
74
  :param wait_before_pid_remove_seconds: float, how many seconds to wait before the process is removed from
30
75
  the pool after process termination event is received for that pid.
76
+ :param process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy, shared dict proxy to update
77
+ the process pool.
78
+ If you run a function from other multiprocessing.Process, you can pass the shared_dict_proxy to the function
79
+ and update the process pool from that function.
80
+
81
+ Example:
82
+ import multiprocessing.managers
83
+
84
+ manager: multiprocessing.managers.SyncManager = multiprocessing.Manager()
85
+ multiprocess_dict_proxy: multiprocessing.managers.DictProxy = manager.dict()
86
+
87
+ process_poller = SimpleProcessPool()
88
+ process_poller.start()
89
+
90
+ while True:
91
+ #============================
92
+ # your function where you get info with pid
93
+ # result = {
94
+ # 'pid': 1234,
95
+ # 'info': 'some info'
96
+ # }
97
+ #============================
98
+ info_with_process_details = {
99
+ 'pid': result['pid'],
100
+ 'info': result['info']
101
+ 'process_details': shared_dict_proxy[result['pid']]
102
+ }
103
+
104
+ break
105
+
106
+ process_poller.stop()
107
+ manager.shutdown()
31
108
  """
32
109
 
33
110
  self.wait_before_pid_remove_seconds: float = wait_before_pid_remove_seconds
111
+ self.process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy = process_pool_shared_dict_proxy
34
112
 
35
113
  self._processes: dict = dict()
36
114
  self._running: bool = False
115
+ self.shared_dict_update_queue: queue.Queue = queue.Queue()
116
+ self.empty_cmdline_queue: queue.Queue = queue.Queue()
37
117
 
38
118
  def start(self):
39
119
  self._running = True
40
120
 
41
121
  self._processes = get_process_list.GetProcessList(
42
- 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)
43
123
 
44
- thread_get_queue = threading.Thread(target=self._start_main_thread)
124
+ thread_get_queue = threading.Thread(target=self._start_main_thread, args=(self.shared_dict_update_queue,))
45
125
  thread_get_queue.daemon = True
46
126
  thread_get_queue.start()
47
127
 
@@ -49,13 +129,21 @@ class SimpleProcessPool:
49
129
  thread_process_termination.daemon = True
50
130
  thread_process_termination.start()
51
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
+
52
140
  def stop(self):
53
141
  self._running = False
54
142
 
55
143
  def get_processes(self):
56
144
  return self._processes
57
145
 
58
- def _start_main_thread(self):
146
+ def _start_main_thread(self, shared_dict_update_queue):
59
147
  get_instance = process_create.ProcessCreateSubscriber()
60
148
  get_instance.start()
61
149
 
@@ -65,12 +153,35 @@ class SimpleProcessPool:
65
153
  process_name = Path(event['NewProcessName']).name
66
154
  command_line = event['CommandLine']
67
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
+
68
161
  self._processes[process_id] = {
69
162
  'name': process_name,
70
163
  'cmdline': command_line
71
164
  }
72
165
 
73
- # print_api(f'Process [{process_id}] added to the pool.', color='blue')
166
+ # Update the multiprocessing shared dict proxy.
167
+ shared_dict_update_queue.put(dict(self._processes))
168
+
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))
74
185
 
75
186
  def _thread_process_termination(self):
76
187
  process_terminate_instance = process_terminate.ProcessTerminateSubscriber()
@@ -110,3 +221,91 @@ class SimpleProcessPool:
110
221
  time.sleep(self.wait_before_pid_remove_seconds)
111
222
  _ = self._processes.pop(process_id, None)
112
223
  # print_api(f'Process [{process_id}] removed from the pool.', color='yellow')
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
+
234
+
235
+ class PidProcessConverter:
236
+ """
237
+ This class is used to get the process details by PID from the process pool shared dict proxy.
238
+ """
239
+
240
+ def __init__(
241
+ self,
242
+ process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy
243
+ ):
244
+ """
245
+ :param process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy, multiprocessing shared dict proxy.
246
+ """
247
+
248
+ self.process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy = process_pool_shared_dict_proxy
249
+
250
+ self.get_process_with_psutil = get_process_list.GetProcessList(get_method='psutil', connect_on_init=True)
251
+
252
+ def get_process_by_pid(self, pid: int):
253
+ """
254
+ THIS FUNCTION WILL RUN OUTSIDE THE PROCESS POOL PROCESS. Inside the function that needs
255
+ the pid to process conversion.
256
+ Get the process details by PID from the process pool shared dict proxy.
257
+
258
+ :param pid: int, the process ID.
259
+ :return: dict, the process details.
260
+ """
261
+
262
+ counter = 0
263
+ process_dict: dict = dict()
264
+ while counter < WAIT_FOR_PROCESS_POLLER_PID_COUNTS:
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:
268
+ # print(dict(self.process_pool_shared_dict_proxy))
269
+ time.sleep(0.1)
270
+ counter += 1
271
+ else:
272
+ process_dict = current_pid_pool[pid]
273
+ break
274
+
275
+ if not process_dict:
276
+ print_api(f"Error: The PID [{pid}] is not in the pool, trying psutil snapshot.", color='yellow')
277
+ # Last resort, try to get the process name by current process snapshot.
278
+ processes = self.get_process_with_psutil.get_processes(as_dict=True)
279
+ if pid not in processes:
280
+ print_api(f"Error: Couldn't get the process name for PID: {pid}.", color='red')
281
+ process_dict = {
282
+ 'name': pid,
283
+ 'cmdline': ''
284
+ }
285
+ else:
286
+ process_dict = processes[pid]
287
+
288
+ return process_dict
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
@@ -113,4 +113,4 @@ def find_and_replace_in_file(
113
113
  for index in found_string_indexes:
114
114
  file_data[index] = file_data[index].replace(single_find.find_what, single_find.replace_to)
115
115
 
116
- file_io.write_file(content=file_data, file_path=file_path, encoding=encoding, convert_list_to_string=True)
116
+ file_io.write_file(content=file_data, file_path=file_path, encoding=encoding)
@@ -1,9 +1,10 @@
1
1
  import sys
2
+ from typing import Union
2
3
 
3
4
  from .print_api import print_api
4
5
 
5
6
 
6
- def get_current_python_version_string() -> str:
7
+ def get_python_version_string() -> str:
7
8
  """
8
9
  Function gets version MAJOR.MINOR.MICRO from 'sys.version_info' object and returns it as a string.
9
10
  :return: python MAJOR.MINOR.MICRO version string.
@@ -12,84 +13,35 @@ def get_current_python_version_string() -> str:
12
13
  return f'{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}'
13
14
 
14
15
 
15
- # noinspection PyUnusedLocal
16
- def check_if_version_object_is_tuple_or_string(version_object: any,
17
- **kwargs) -> tuple:
18
- """
19
- Function checks if 'version_object' that was passed is tuple or string.
20
- If it's tuple then returns it back. If it's string, converts to tuple then returns it.
21
- If the object is none of the above, returns None.
22
-
23
- :param version_object: Can be string ('3.10') or tuple of integers ((3, 10)).
24
- :return:
25
- """
26
- # Check if tuple was passed.
27
- if isinstance(version_object, tuple):
28
- return version_object
29
- else:
30
- # Then check if a string was passed.
31
- if isinstance(version_object, str):
32
- # The check will be against tuple of integers, so we'll convert a string to tuple of integers.
33
- return tuple(map(int, version_object.split('.')))
34
- else:
35
- message = f'[*] Function: [check_if_version_object_is_tuple_or_string]\n' \
36
- f'[*] [version_object] object passed is not tuple or string.\n' \
37
- f'[*] Object type: {type(version_object)}\n' \
38
- f'[*] Object content {version_object}\n' \
39
- f'Exiting...'
40
- print_api(message, error_type=True, logger_method='critical', **kwargs)
41
-
42
- return None
43
-
44
-
45
- # noinspection PyUnusedLocal
46
- def check_python_version_compliance(minimum_version: any,
47
- maximum_version: any = None,
48
- **kwargs) -> bool:
16
+ def check_python_version_compliance(
17
+ min_ver: tuple = None,
18
+ max_ver: tuple = None,
19
+ ) -> str | None:
49
20
  """
50
21
  Python version check. Should be executed before importing external libraries, since they depend on Python version.
51
22
 
52
- :param minimum_version: Can be string ('3.10') or tuple of integers ((3, 10)).
53
- :param maximum_version: Can be string ('3.10') or tuple of integers ((3, 10)).
23
+ :param min_ver: tuple of integers (3, 10).
24
+ :param max_ver: tuple of integers (3, 10).
54
25
  If maximum version is not specified, it will be considered as all versions above the minimum are compliant.
55
- :return:
26
+ :return: If version is not compliant, returns string with error message. Otherwise, returns None.
56
27
  """
57
28
 
58
- # Check 'minimum_version' object for string or tuple and get the tuple.
59
- minimum_version_scheme: tuple = check_if_version_object_is_tuple_or_string(minimum_version, **kwargs)
60
- # If 'maximum_version' object was passed, check it for string or tuple and get the tuple.
61
- if maximum_version:
62
- maximum_version_scheme: tuple = check_if_version_object_is_tuple_or_string(maximum_version, **kwargs)
63
-
64
- # Get current python version.
65
- python_version_full: str = get_current_python_version_string()
66
-
67
- message = f"[*] Current Python Version: {python_version_full}"
68
- print_api(message, logger_method='info', **kwargs)
69
-
70
- # if 'maximum_version' passed and current python version is later or equals to the minimum and earlier than
71
- # maximum version required.
72
- if maximum_version:
73
- if not sys.version_info >= minimum_version_scheme or not sys.version_info < maximum_version_scheme:
74
- message = f"[!!!] YOU NEED TO INSTALL AT LEAST PYTHON " \
75
- f"{'.'.join(str(i) for i in minimum_version_scheme)}, " \
76
- f"AND EARLIER THAN {'.'.join(str(i) for i in maximum_version_scheme)}, " \
77
- f"to work properly. Unhandled exceptions are inevitable!"
78
- print_api(message, error_type=True, logger_method='critical', **kwargs)
79
-
80
- return False
81
- # If 'maximum_version' wasn't passed.
29
+ if not min_ver and not max_ver:
30
+ raise ValueError("At least one of the version parameters should be passed.")
31
+
32
+ current_version_info: tuple = sys.version_info[:3]
33
+ if min_ver and not max_ver:
34
+ if current_version_info < min_ver:
35
+ return f'Python version {".".join(map(str, min_ver))} or higher is required. '\
36
+ f'Current version is {".".join(map(str, current_version_info))}.'
37
+ elif max_ver and not min_ver:
38
+ if current_version_info > max_ver:
39
+ return f'Python version up to {".".join(map(str, max_ver))} is required. '\
40
+ f'Current version is {".".join(map(str, current_version_info))}.'
41
+ elif min_ver and max_ver:
42
+ if not (min_ver <= current_version_info <= max_ver):
43
+ return f'Python version between {".".join(map(str, min_ver))} and '\
44
+ f'{".".join(map(str, max_ver))} is required. '\
45
+ f'Current version is {".".join(map(str, current_version_info))}.'
82
46
  else:
83
- # Check if current python version is later or equals to the minimum required version.
84
- if not sys.version_info >= minimum_version_scheme:
85
- message = f"[!!!] YOU NEED TO INSTALL AT LEAST PYTHON " \
86
- f"{'.'.join(str(i) for i in minimum_version_scheme)}, " \
87
- f"to work properly. Unhandled exceptions are inevitable!"
88
- print_api(message, error_type=True, logger_method='critical', **kwargs)
89
-
90
- return False
91
-
92
- message = "[*] Version Check PASSED."
93
- print_api(message, logger_method='info', **kwargs)
94
-
95
- return True
47
+ return None
@@ -1,3 +1,11 @@
1
+ # TODO: Change manual wrapper to:
2
+ # from ffmpy import FFmpeg
3
+ # ff = FFmpeg(
4
+ # inputs={'input.mp4': None},
5
+ # outputs={'output.avi': None}
6
+ # )
7
+ # ff.run()
8
+
1
9
  from .wrappers.ffmpegw import FFmpegWrapper
2
10
  from .tempfiles import TempFile
3
11
  from .web import download