atomicshop 2.11.47__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 (268) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
  4. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  5. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  6. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  7. atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
  8. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  9. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  10. atomicshop/a_mains/github_wrapper.py +11 -0
  11. atomicshop/a_mains/install_ca_certificate.py +172 -0
  12. atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
  13. atomicshop/a_mains/process_from_port.py +119 -0
  14. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  15. atomicshop/a_mains/update_config_toml.py +38 -0
  16. atomicshop/appointment_management.py +5 -3
  17. atomicshop/basics/ansi_escape_codes.py +3 -1
  18. atomicshop/basics/argparse_template.py +2 -0
  19. atomicshop/basics/booleans.py +27 -30
  20. atomicshop/basics/bytes_arrays.py +43 -0
  21. atomicshop/basics/classes.py +149 -1
  22. atomicshop/basics/dicts.py +12 -0
  23. atomicshop/basics/enums.py +2 -2
  24. atomicshop/basics/exceptions.py +5 -1
  25. atomicshop/basics/list_of_classes.py +29 -0
  26. atomicshop/basics/list_of_dicts.py +69 -5
  27. atomicshop/basics/lists.py +14 -0
  28. atomicshop/basics/multiprocesses.py +374 -50
  29. atomicshop/basics/package_module.py +10 -0
  30. atomicshop/basics/strings.py +160 -7
  31. atomicshop/basics/threads.py +14 -0
  32. atomicshop/basics/tracebacks.py +13 -4
  33. atomicshop/certificates.py +153 -52
  34. atomicshop/config_init.py +12 -7
  35. atomicshop/console_user_response.py +7 -14
  36. atomicshop/consoles.py +9 -0
  37. atomicshop/datetimes.py +98 -0
  38. atomicshop/diff_check.py +340 -40
  39. atomicshop/dns.py +128 -12
  40. atomicshop/etws/_pywintrace_fix.py +17 -0
  41. atomicshop/etws/const.py +38 -0
  42. atomicshop/etws/providers.py +21 -0
  43. atomicshop/etws/sessions.py +43 -0
  44. atomicshop/etws/trace.py +168 -0
  45. atomicshop/etws/traces/trace_dns.py +162 -0
  46. atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
  47. atomicshop/etws/traces/trace_tcp.py +130 -0
  48. atomicshop/file_io/csvs.py +222 -24
  49. atomicshop/file_io/docxs.py +35 -18
  50. atomicshop/file_io/file_io.py +35 -19
  51. atomicshop/file_io/jsons.py +49 -0
  52. atomicshop/file_io/tomls.py +139 -0
  53. atomicshop/filesystem.py +864 -293
  54. atomicshop/get_process_list.py +133 -0
  55. atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
  56. atomicshop/http_parse.py +149 -93
  57. atomicshop/ip_addresses.py +6 -1
  58. atomicshop/mitm/centered_settings.py +132 -0
  59. atomicshop/mitm/config_static.py +207 -0
  60. atomicshop/mitm/config_toml_editor.py +55 -0
  61. atomicshop/mitm/connection_thread_worker.py +875 -357
  62. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  63. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  64. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  65. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  66. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  67. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  68. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  69. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  70. atomicshop/mitm/engines/create_module_template.py +58 -14
  71. atomicshop/mitm/import_config.py +359 -139
  72. atomicshop/mitm/initialize_engines.py +160 -74
  73. atomicshop/mitm/message.py +64 -23
  74. atomicshop/mitm/mitm_main.py +892 -0
  75. atomicshop/mitm/recs_files.py +183 -0
  76. atomicshop/mitm/shared_functions.py +4 -10
  77. atomicshop/mitm/ssh_tester.py +82 -0
  78. atomicshop/mitm/statistic_analyzer.py +257 -166
  79. atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
  80. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
  81. atomicshop/monitor/change_monitor.py +96 -120
  82. atomicshop/monitor/checks/dns.py +139 -70
  83. atomicshop/monitor/checks/file.py +77 -0
  84. atomicshop/monitor/checks/network.py +81 -77
  85. atomicshop/monitor/checks/process_running.py +33 -34
  86. atomicshop/monitor/checks/url.py +94 -0
  87. atomicshop/networks.py +671 -0
  88. atomicshop/on_exit.py +205 -0
  89. atomicshop/package_mains_processor.py +84 -0
  90. atomicshop/permissions/permissions.py +22 -0
  91. atomicshop/permissions/ubuntu_permissions.py +239 -0
  92. atomicshop/permissions/win_permissions.py +33 -0
  93. atomicshop/print_api.py +24 -41
  94. atomicshop/process.py +63 -17
  95. atomicshop/process_poller/__init__.py +0 -0
  96. atomicshop/process_poller/pollers/__init__.py +0 -0
  97. atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
  98. atomicshop/process_poller/process_pool.py +207 -0
  99. atomicshop/process_poller/simple_process_pool.py +311 -0
  100. atomicshop/process_poller/tracer_base.py +45 -0
  101. atomicshop/process_poller/tracers/__init__.py +0 -0
  102. atomicshop/process_poller/tracers/event_log.py +46 -0
  103. atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
  104. atomicshop/python_file_patcher.py +1 -1
  105. atomicshop/python_functions.py +27 -75
  106. atomicshop/question_answer_engine.py +2 -2
  107. atomicshop/scheduling.py +24 -5
  108. atomicshop/sound.py +4 -2
  109. atomicshop/speech_recognize.py +8 -0
  110. atomicshop/ssh_remote.py +158 -172
  111. atomicshop/startup/__init__.py +0 -0
  112. atomicshop/startup/win/__init__.py +0 -0
  113. atomicshop/startup/win/startup_folder.py +53 -0
  114. atomicshop/startup/win/task_scheduler.py +119 -0
  115. atomicshop/system_resource_monitor.py +61 -46
  116. atomicshop/system_resources.py +8 -8
  117. atomicshop/tempfiles.py +1 -2
  118. atomicshop/timer.py +30 -11
  119. atomicshop/urls.py +41 -0
  120. atomicshop/venvs.py +28 -0
  121. atomicshop/versioning.py +27 -0
  122. atomicshop/web.py +110 -25
  123. atomicshop/web_apis/__init__.py +0 -0
  124. atomicshop/web_apis/google_custom_search.py +44 -0
  125. atomicshop/web_apis/google_llm.py +188 -0
  126. atomicshop/websocket_parse.py +450 -0
  127. atomicshop/wrappers/certauthw/certauth.py +1 -0
  128. atomicshop/wrappers/cryptographyw.py +29 -8
  129. atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
  130. atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
  131. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
  132. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  133. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  134. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
  135. atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
  136. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  137. atomicshop/wrappers/ctyping/win_console.py +39 -0
  138. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  139. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  140. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  141. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  142. atomicshop/wrappers/factw/get_file_data.py +12 -5
  143. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  144. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  145. atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
  146. atomicshop/wrappers/githubw.py +583 -51
  147. atomicshop/wrappers/loggingw/consts.py +49 -0
  148. atomicshop/wrappers/loggingw/filters.py +102 -0
  149. atomicshop/wrappers/loggingw/formatters.py +58 -71
  150. atomicshop/wrappers/loggingw/handlers.py +459 -40
  151. atomicshop/wrappers/loggingw/loggers.py +19 -0
  152. atomicshop/wrappers/loggingw/loggingw.py +1010 -178
  153. atomicshop/wrappers/loggingw/reading.py +344 -19
  154. atomicshop/wrappers/mongodbw/__init__.py +0 -0
  155. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  156. atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
  157. atomicshop/wrappers/netshw.py +271 -0
  158. atomicshop/wrappers/playwrightw/engine.py +34 -19
  159. atomicshop/wrappers/playwrightw/infra.py +5 -0
  160. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  161. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  162. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  163. atomicshop/wrappers/playwrightw/waits.py +9 -7
  164. atomicshop/wrappers/powershell_networking.py +80 -0
  165. atomicshop/wrappers/psutilw/processes.py +81 -0
  166. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  167. atomicshop/wrappers/psutilw/psutilw.py +9 -0
  168. atomicshop/wrappers/pyopensslw.py +9 -2
  169. atomicshop/wrappers/pywin32w/__init__.py +0 -0
  170. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  171. atomicshop/wrappers/pywin32w/console.py +34 -0
  172. atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
  173. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  174. atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
  175. atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
  176. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
  177. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
  178. atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
  179. atomicshop/wrappers/pywin32w/winshell.py +19 -0
  180. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  181. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  182. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  183. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  184. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  185. atomicshop/wrappers/socketw/accepter.py +21 -7
  186. atomicshop/wrappers/socketw/certificator.py +216 -150
  187. atomicshop/wrappers/socketw/creator.py +190 -50
  188. atomicshop/wrappers/socketw/dns_server.py +500 -173
  189. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  190. atomicshop/wrappers/socketw/process_getter.py +86 -0
  191. atomicshop/wrappers/socketw/receiver.py +144 -102
  192. atomicshop/wrappers/socketw/sender.py +65 -35
  193. atomicshop/wrappers/socketw/sni.py +334 -165
  194. atomicshop/wrappers/socketw/socket_base.py +134 -0
  195. atomicshop/wrappers/socketw/socket_client.py +137 -95
  196. atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
  197. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  198. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  199. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  200. atomicshop/wrappers/sysmonw.py +157 -0
  201. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  202. atomicshop/wrappers/win_auditw.py +189 -0
  203. atomicshop/wrappers/winregw/__init__.py +0 -0
  204. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  205. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  206. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
  207. atomicshop-3.10.5.dist-info/RECORD +306 -0
  208. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  209. atomicshop/_basics_temp.py +0 -101
  210. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  211. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  212. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  213. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  214. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  215. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  216. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  217. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  218. atomicshop/addons/package_setup/Setup.cmd +0 -7
  219. atomicshop/addons/process_list/compile.cmd +0 -2
  220. atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  221. atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  222. atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  223. atomicshop/archiver/_search_in_zip.py +0 -189
  224. atomicshop/archiver/archiver.py +0 -34
  225. atomicshop/archiver/search_in_archive.py +0 -250
  226. atomicshop/archiver/sevenz_app_w.py +0 -86
  227. atomicshop/archiver/sevenzs.py +0 -44
  228. atomicshop/archiver/zips.py +0 -293
  229. atomicshop/etw/dns_trace.py +0 -118
  230. atomicshop/etw/etw.py +0 -61
  231. atomicshop/file_types.py +0 -24
  232. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  233. atomicshop/mitm/initialize_mitm_server.py +0 -240
  234. atomicshop/monitor/checks/hash.py +0 -44
  235. atomicshop/monitor/checks/hash_checks/file.py +0 -55
  236. atomicshop/monitor/checks/hash_checks/url.py +0 -62
  237. atomicshop/pbtkmultifile_argparse.py +0 -88
  238. atomicshop/permissions.py +0 -110
  239. atomicshop/process_poller.py +0 -237
  240. atomicshop/script_as_string_processor.py +0 -38
  241. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  242. atomicshop/ssh_scripts/process_from_port.py +0 -27
  243. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  244. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  245. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  246. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  247. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  248. atomicshop/wrappers/ffmpegw.py +0 -125
  249. atomicshop/wrappers/loggingw/checks.py +0 -20
  250. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  251. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  252. atomicshop/wrappers/socketw/base.py +0 -59
  253. atomicshop/wrappers/socketw/get_process.py +0 -107
  254. atomicshop/wrappers/wslw.py +0 -191
  255. atomicshop-2.11.47.dist-info/RECORD +0 -251
  256. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  257. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  258. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  259. /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  260. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  261. /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
  262. /atomicshop/{archiver → etws}/__init__.py +0 -0
  263. /atomicshop/{etw → etws/traces}/__init__.py +0 -0
  264. /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
  265. /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
  266. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  267. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  268. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
atomicshop/on_exit.py ADDED
@@ -0,0 +1,205 @@
1
+ import atexit
2
+ import signal
3
+ import sys
4
+ import platform
5
+
6
+ import win32api
7
+ import win32con
8
+
9
+ from .print_api import print_api
10
+ from .wrappers.pywin32w import console
11
+
12
+
13
+ EXIT_HANDLER_INSTANCE = None
14
+
15
+
16
+ class ExitHandler:
17
+ """
18
+ This class is used to handle exit events: Closing the console, pressing 'CTRL+C', Killing the process.
19
+
20
+ Example:
21
+ from atomicshop import on_exit
22
+
23
+
24
+ def clean_up_function():
25
+ print("Cleaning up.")
26
+
27
+
28
+ on_exit.ExitHandler(clean_up_function).register_handlers()
29
+
30
+ # OR
31
+ on_exit.register_exit_handler(clean_up_function)
32
+ """
33
+ def __init__(
34
+ self,
35
+ cleanup_action: callable,
36
+ args: tuple = None,
37
+ kwargs: dict = None
38
+ ):
39
+ """
40
+ :param cleanup_action: The action to run when one of exit types is triggered.
41
+ :param args: The arguments to pass to the cleanup action.
42
+ :param kwargs: The keyword arguments to pass to the cleanup action.
43
+ """
44
+
45
+ if not callable(cleanup_action):
46
+ raise ValueError("The 'cleanup_action' must be a callable function.")
47
+
48
+ if args is None:
49
+ args = tuple()
50
+
51
+ if kwargs is None:
52
+ kwargs = dict()
53
+
54
+ self.cleanup_action: callable = cleanup_action
55
+ self.args: tuple = args
56
+ self.kwargs: dict = kwargs
57
+
58
+ self._called: bool = False
59
+ self._handler_hit: bool = False
60
+
61
+ def _run_cleanup(self):
62
+ if not self._called:
63
+ self._called = True
64
+ self.cleanup_action(*self.args, **self.kwargs)
65
+
66
+ def console_handler(self, event):
67
+ if event == win32con.CTRL_CLOSE_EVENT:
68
+
69
+ if not self._handler_hit:
70
+ self._handler_hit = True
71
+
72
+ print("Console close event.")
73
+ self._run_cleanup()
74
+
75
+ return True
76
+ return False
77
+
78
+ def atexit_handler(self):
79
+ if not self._handler_hit:
80
+ self._handler_hit = True
81
+
82
+ print("atexit_handler")
83
+ self._run_cleanup()
84
+
85
+ def signal_handler(self, signum, frame):
86
+ if not self._handler_hit:
87
+ self._handler_hit = True
88
+
89
+ print_api(f"Signal {signum}")
90
+ self._run_cleanup()
91
+ # Exit the process gracefully
92
+ raise SystemExit(0)
93
+
94
+ def register_handlers(
95
+ self,
96
+ at_exit: bool = True,
97
+ console_close: bool = True,
98
+ kill_signal: bool = True
99
+ ):
100
+ """
101
+ Register the exit handlers.
102
+
103
+ :param at_exit: Register the atexit handler.
104
+ Just remember that the atexit handler will be called right away on [Ctrl+C], meaning if you want to do
105
+ something specifically on KeyboardInterrupt, you should handle it separately and set this parameter to False.
106
+ Same goes for all the exceptions.
107
+ :param console_close: Register the console close handler.
108
+ :param kill_signal: Register the kill signal handler.
109
+ """
110
+ if at_exit:
111
+ atexit.register(self.atexit_handler)
112
+ if console_close:
113
+ win32api.SetConsoleCtrlHandler(self.console_handler, True)
114
+ if kill_signal:
115
+ signal.signal(signal.SIGINT, self.signal_handler)
116
+ signal.signal(signal.SIGTERM, self.signal_handler)
117
+
118
+
119
+ def register_exit_handler(
120
+ clean_up_function,
121
+ at_exit: bool = True,
122
+ console_close: bool = True,
123
+ kill_signal: bool = True,
124
+ *args, **kwargs):
125
+ """
126
+ This function will register the exit handler to handle exit events: Closing the console, pressing 'CTRL+C',
127
+ Killing the process.
128
+
129
+ :param clean_up_function: The action to run when one of exit types is triggered.
130
+ :param at_exit: Register the atexit handler.
131
+ Just remember that the atexit handler will be called right away on [Ctrl+C], meaning if you want to do something
132
+ specifically on KeyboardInterrupt, you should handle it separately and set this parameter to False.
133
+ Same goes for all the exceptions.
134
+ :param console_close: Register the console close handler.
135
+ :param kill_signal: Register the kill signal handler.
136
+ Same problem as with atexit handler, it will be called right away on [Ctrl+C].
137
+ :param args: The arguments to pass to the cleanup action.
138
+ :param kwargs: The keyword arguments to pass to the cleanup action.
139
+ """
140
+ global EXIT_HANDLER_INSTANCE
141
+ EXIT_HANDLER_INSTANCE = ExitHandler(clean_up_function, args, kwargs)
142
+ EXIT_HANDLER_INSTANCE.register_handlers(at_exit=at_exit, console_close=console_close, kill_signal=kill_signal)
143
+
144
+
145
+ def restart_function(callable_function, *args, **kwargs):
146
+ """
147
+ This function will run the callable function with the given arguments and keyword arguments.
148
+ If the function raises an exception, the function will be restarted.
149
+
150
+ :param callable_function: The function to run.
151
+ :param args: The arguments to pass to the function.
152
+ :param kwargs: The keyword arguments to pass to the function.
153
+ :return: The return value of the function.
154
+ """
155
+ while True:
156
+ try:
157
+ return callable_function(*args, **kwargs)
158
+ except Exception as e:
159
+ print(f"ERROR: {e}")
160
+ continue
161
+
162
+
163
+ def _ref_run_callable_on_exit_and_signals(
164
+ callable_function,
165
+ print_kwargs: dict = None,
166
+ *args,
167
+ **kwargs
168
+ ):
169
+ """
170
+ THIS IS FOR REFERENCE ONLY.
171
+
172
+ This function will run the callable function with the given arguments and keyword arguments.
173
+ If the function raises an exception, the function will be restarted.
174
+
175
+ :param callable_function: The function to run.
176
+ :param print_kwargs: print_api kwargs.
177
+ :param args: The arguments to pass to the function.
178
+ :param kwargs: The keyword arguments to pass to the function.
179
+ :return: The return value of the function.
180
+ """
181
+ def signal_handler(signum, frame):
182
+ print_api(f"Signal {signum} received, exiting.", **(print_kwargs or {}))
183
+ callable_function(*args, **kwargs)
184
+ sys.exit(0)
185
+
186
+ def exit_handler():
187
+ print_api("Exiting.", **(print_kwargs or {}))
188
+ callable_function(*args, **kwargs)
189
+ sys.exit(0)
190
+
191
+ signals = [signal.SIGINT, signal.SIGTERM]
192
+ if platform.system() != 'Windows':
193
+ signals.append(signal.SIGQUIT)
194
+ signals.append(signal.SIGHUP)
195
+ for sig in signals:
196
+ signal.signal(sig, signal_handler)
197
+
198
+ # signal.signal(signal.SIGINT, signal_handler)
199
+ # signal.signal(signal.SIGTERM, signal_handler)
200
+ atexit.register(exit_handler)
201
+
202
+ # Register console handler for Windows.
203
+ if platform.system() == 'Windows':
204
+ console_handler = console.ConsoleHandler(exit_handler, args, kwargs)
205
+ console_handler.register_handler()
@@ -0,0 +1,84 @@
1
+ """Loading resources using stdlib importlib.resources APIs (Python 3.7+)
2
+ https://docs.python.org/3/library/importlib.html#module-importlib.resources"""
3
+ import importlib.resources
4
+ from contextlib import redirect_stdout
5
+ import io
6
+ import subprocess
7
+ import sys
8
+ from typing import Callable
9
+
10
+
11
+ class PackageMainsProcessor:
12
+ def __init__(
13
+ self,
14
+ script_file_stem: str = None
15
+ ):
16
+ self.script_file_stem: str = script_file_stem
17
+ self.resources_directory_name: str = 'a_mains'
18
+
19
+ def get_resource_path(self) -> str:
20
+ return f'{__package__}.{self.resources_directory_name}'
21
+
22
+ def read_script_file_to_string(self) -> str:
23
+ script_string = importlib.resources.read_text(self.get_resource_path(), f'{self.script_file_stem}.py')
24
+
25
+ return script_string
26
+
27
+ def execute_script_file(
28
+ self,
29
+ function_name: str = 'main',
30
+ args: tuple = None,
31
+ kwargs: dict = None,
32
+ get_printed_output: bool = False
33
+ ) -> str:
34
+ """
35
+ Execute a script file from the package resources and get result as string.
36
+
37
+ :param function_name: Name of the function to call within the script.
38
+ :param args: Tuple of positional arguments to pass to the function.
39
+ :param kwargs: Dictionary of keyword arguments to pass to the function.
40
+ :param get_printed_output: If True, captures and returns printed output instead of return value.
41
+
42
+ :return: Output of the script execution as a string.
43
+ """
44
+
45
+ if not args:
46
+ args = ()
47
+ if not kwargs:
48
+ kwargs = {}
49
+
50
+ module_name = f"{self.get_resource_path()}.{self.script_file_stem}" # script_file_name WITHOUT ".py"
51
+
52
+ module = importlib.import_module(module_name)
53
+ callable_function: Callable = getattr(module, function_name)
54
+
55
+ if get_printed_output:
56
+ with io.StringIO() as buffer, redirect_stdout(buffer):
57
+ callable_function(*args, **kwargs)
58
+ result = buffer.getvalue()
59
+ else:
60
+ result = callable_function(*args, **kwargs)
61
+
62
+ return result
63
+
64
+ def execute_script_with_subprocess(
65
+ self,
66
+ arguments: list = None
67
+ ) -> tuple[str, str, int]:
68
+ """
69
+ Execute a script file from the package resources using subprocess and get result as string.
70
+ :param arguments: Dictionary of arguments to pass to the script.
71
+ Example: ['--port', '8080', '-v']
72
+ :return: Tuple containing (stdout, stderr, returncode).
73
+ """
74
+
75
+ # script_file_name WITHOUT ".py"
76
+ module_name = f"{self.get_resource_path()}.{self.script_file_stem}"
77
+
78
+ command = [sys.executable, "-m", module_name]
79
+ if arguments:
80
+ command.extend(arguments)
81
+
82
+ result = subprocess.run(command, capture_output=True, text=True)
83
+
84
+ return result.stdout, result.stderr, result.returncode
@@ -0,0 +1,22 @@
1
+ import os
2
+ import ctypes
3
+
4
+
5
+ def is_admin() -> bool:
6
+ """
7
+ Function checks on Windows or POSIX OSes if the script is executed under Administrative Privileges.
8
+ :return: True / False.
9
+ """
10
+
11
+ if os.name == 'nt':
12
+ if ctypes.windll.shell32.IsUserAnAdmin() == 0:
13
+ result = False
14
+ else:
15
+ result = True
16
+ else:
17
+ if 'SUDO_USER' in os.environ and os.geteuid() == 0:
18
+ result = True
19
+ else:
20
+ result = False
21
+
22
+ return result
@@ -0,0 +1,239 @@
1
+ import os
2
+ import stat
3
+ import contextlib
4
+ import subprocess
5
+ import getpass
6
+
7
+ # Import pwd only on linux.
8
+ if os.name == 'posix':
9
+ import pwd
10
+
11
+
12
+ def get_sudo_executer_username() -> str:
13
+ """
14
+ Function gets the username of the user who executed the script with sudo.
15
+ :return: str, username.
16
+ """
17
+
18
+ if 'SUDO_USER' in os.environ:
19
+ return os.environ['SUDO_USER']
20
+ else:
21
+ return ''
22
+
23
+
24
+ def detect_current_user(
25
+ optional_env_user_var: str = 'CUSTOM_SCRIPTED_USER'
26
+ ) -> str:
27
+ """
28
+ Try to robustly determine the 'real' installing user.
29
+
30
+ Priority:
31
+ 1. FDB_INSTALL_USER env var (explicit override).
32
+ 2. If running as root with sudo: use SUDO_USER.
33
+ 3. Otherwise: use effective uid.
34
+ 4. Fallbacks: getpass.getuser() / $USER.
35
+
36
+ :param optional_env_user_var: str, name of the environment variable that can override the user detection.
37
+ :return: str, username.
38
+ """
39
+
40
+ # 1. Explicit override for weird environments (CI, containers, etc.)
41
+ env_user = os.getenv(optional_env_user_var)
42
+ if env_user:
43
+ return env_user
44
+
45
+ # 2. If we are root, prefer the sudo caller if any
46
+ try:
47
+ euid = os.geteuid()
48
+ except AttributeError: # non-POSIX, very unlikely here
49
+ euid = None
50
+
51
+ if euid == 0:
52
+ sudo_user = os.environ.get("SUDO_USER")
53
+ if sudo_user:
54
+ return sudo_user
55
+
56
+ # 3. Normal case: effective uid -> username
57
+ if euid is not None:
58
+ try:
59
+ return pwd.getpwuid(euid).pw_name
60
+ except Exception:
61
+ pass
62
+
63
+ # 4. Fallbacks that don’t depend on utmp/tty
64
+ try:
65
+ return getpass.getuser()
66
+ except Exception:
67
+ return os.environ.get("USER", "unknown")
68
+
69
+
70
+ def set_executable(file_path: str):
71
+ """
72
+ Function sets the executable permission on a file.
73
+ Equivalent to: chmod +x <file_path>
74
+
75
+ :param file_path: str, path to the file.
76
+ :return:
77
+ """
78
+
79
+ # os.chmod(file_path, os.stat(file_path).st_mode | 0o111)
80
+ os.chmod(file_path, os.stat(file_path).st_mode | stat.S_IXUSR)
81
+
82
+
83
+ def set_trusted_executable(file_path: str):
84
+ """
85
+ Function sets the executable permission on a file and marks it as trusted.
86
+ :param file_path: str, path to the file.
87
+ :return:
88
+ """
89
+
90
+ # Check if the file exists
91
+ if not os.path.exists(file_path):
92
+ raise FileNotFoundError(f"The file does not exist: {file_path} ")
93
+
94
+ # Execute the `gio set` command
95
+ subprocess.run(
96
+ ["gio", "set", file_path, "metadata::trusted", "true"],
97
+ check=True
98
+ )
99
+
100
+
101
+ def set_xfce_exe_checksum(desktop_file_path):
102
+ # Expand `~` to the full home directory path
103
+ desktop_file_path = os.path.expanduser(desktop_file_path)
104
+
105
+ # Ensure the file exists
106
+ if not os.path.exists(desktop_file_path):
107
+ raise FileNotFoundError(f"The file does not exist: {desktop_file_path} ")
108
+
109
+ # Calculate the SHA256 checksum of the file
110
+ result = subprocess.run(
111
+ ["sha256sum", desktop_file_path],
112
+ stdout=subprocess.PIPE,
113
+ check=True,
114
+ text=True
115
+ )
116
+ checksum = result.stdout.split()[0]
117
+
118
+ # Set the metadata::xfce-exe-checksum attribute using `gio`
119
+ subprocess.run(
120
+ ["gio", "set", "-t", "string", desktop_file_path, "metadata::xfce-exe-checksum", checksum],
121
+ check=True
122
+ )
123
+
124
+
125
+ def change_file_owner(file_path: str, username: str):
126
+ """
127
+ Function changes the owner of the file to the specified user.
128
+ :param file_path: str, path to the file.
129
+ :param username: str, username of the new owner.
130
+ :return:
131
+ """
132
+
133
+ uid = pwd.getpwnam(username).pw_uid
134
+ os.chown(file_path, uid, -1)
135
+
136
+
137
+ def is_executable(file_path: str) -> bool:
138
+ """
139
+ Function checks if the file has the executable permission.
140
+ Equivalent to: stat -c "%a %n" <file_path>
141
+
142
+ :param file_path: str, path to the file.
143
+ :return: bool, True / False.
144
+ """
145
+
146
+ return bool(os.stat(file_path).st_mode & stat.S_IXUSR)
147
+
148
+
149
+ def run_as_root(command):
150
+ subprocess.check_call(['sudo'] + command)
151
+
152
+
153
+ @contextlib.contextmanager
154
+ def temporary_regular_permissions():
155
+ """
156
+ This function is used to temporarily change the effective user and group ID to the original user's.
157
+ This is used to run commands with the original user's permissions.
158
+ If you executed a script with 'sudo' and wanted certain action to execute as regular user and not root.
159
+
160
+ Example:
161
+ with temporary_regular_permissions():
162
+ # Do something with regular permissions.
163
+ pass
164
+
165
+ :return:
166
+ """
167
+ # Save the current effective user and group ID
168
+ original_euid, original_egid = os.geteuid(), os.getegid()
169
+
170
+ try:
171
+ # Get the original user's UID and GID
172
+ orig_uid = int(os.environ.get('SUDO_UID', os.getuid()))
173
+ orig_gid = int(os.environ.get('SUDO_GID', os.getgid()))
174
+
175
+ # Set the effective user and group ID to the original user's
176
+ os.setegid(orig_gid)
177
+ os.seteuid(orig_uid)
178
+
179
+ # Provide the context to do something with these permissions
180
+ yield
181
+ finally:
182
+ # Revert to the original effective user and group ID
183
+ os.seteuid(original_euid)
184
+ os.setegid(original_egid)
185
+
186
+
187
+ def expand_user_path(user_name, path):
188
+ pwnam = pwd.getpwnam(user_name)
189
+ home_dir = pwnam.pw_dir
190
+ return path.replace("~", home_dir)
191
+
192
+
193
+ def set_folder_permissions(
194
+ folder_path: str,
195
+ username: str = None,
196
+ logged_in_non_sudo_user: bool = False
197
+ ):
198
+ """
199
+ Set ownership and permissions for an existing folder.
200
+
201
+ :param folder_path: Path to the folder (must already exist)
202
+ :param username: Username to assign ownership to (ignored if non_sudo_user=True)
203
+ :param logged_in_non_sudo_user: If True, use the current logged-in user unless running under sudo
204
+ """
205
+
206
+ if not username and not logged_in_non_sudo_user:
207
+ raise ValueError("A username must be provided, or 'non_sudo_user' must be set to True.")
208
+
209
+ # Handle non_sudo_user case
210
+ if logged_in_non_sudo_user:
211
+ # Get the current logged-in user
212
+ username = pwd.getpwuid(os.getuid())[0]
213
+
214
+ # Get the UID and GID of the specified user
215
+ user_info = pwd.getpwnam(username)
216
+ user_uid = user_info.pw_uid
217
+ user_gid = user_info.pw_gid
218
+
219
+ # Change ownership of the folder to the specified user
220
+ # print(f"Changing ownership of {folder_path} to user '{username}'...")
221
+ os.chown(folder_path, user_uid, user_gid)
222
+
223
+ # Set appropriate permissions (read, write, execute for the owner)
224
+ # print(f"Setting permissions for {folder_path}...")
225
+ os.chmod(folder_path, 0o755) # Owner rwx, group r-x, others r-x
226
+
227
+ # print(f"Ownership and permissions updated for folder: '{folder_path}'")
228
+
229
+
230
+ def is_directory_owner(directory_path: str, username: str) -> bool:
231
+ """
232
+ Function checks if the directory is owned by the specified user.
233
+ :param directory_path: str, path to the directory.
234
+ :param username: str, username of the user.
235
+ :return: bool, True / False.
236
+ """
237
+
238
+ uid = pwd.getpwnam(username).pw_uid
239
+ return os.stat(directory_path).st_uid == uid
@@ -0,0 +1,33 @@
1
+ import subprocess
2
+
3
+
4
+ def unblock_file_windows(file_path):
5
+ """
6
+ Unblock a file on Windows. This is used to unblock files downloaded from the internet.
7
+ When you Right-click then navigate to Properties, you will see the Unblock checkbox.
8
+ :param file_path:
9
+ :return:
10
+ """
11
+ try:
12
+ subprocess.run(["powershell", "-Command", f"Unblock-File -Path '{file_path}'"], check=True)
13
+ print(f"Successfully unblocked the file: {file_path}")
14
+ except subprocess.CalledProcessError as e:
15
+ print(f"Failed to unblock the file: {file_path}\nError: {e}")
16
+
17
+
18
+ def get_command_to_run_as_admin_windows(command: str) -> str:
19
+ """
20
+ Function returns the command to run a command as administrator on Windows.
21
+ NOTE: When you run something this way, the parent will be the powershell.exe process.
22
+ If you need a status result directly of the executed command, you need to use subprocess.run() instead.
23
+
24
+ :param command: str, command to run.
25
+ :return: str, command to run as administrator.
26
+ """
27
+
28
+ executable = command.split()[0]
29
+ command = (
30
+ f"powershell -Command "
31
+ f"\"Start-Process {executable} -ArgumentList '{' '.join(command.split()[1:])}' -Verb RunAs\"")
32
+
33
+ return command