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
@@ -1,8 +1,9 @@
1
1
  from typing import Union
2
2
  import functools
3
+ import os
3
4
 
4
- from ..print_api import print_api
5
- from ..inspect_wrapper import get_target_function_default_args_and_combine_with_current
5
+ from .. import print_api
6
+ from .. import inspect_wrapper
6
7
 
7
8
 
8
9
  def get_write_file_mode_string_from_overwrite_bool(overwrite: bool) -> str:
@@ -17,9 +18,20 @@ def write_file_decorator(function_name):
17
18
  def wrapper_write_file_decorator(*args, **kwargs):
18
19
  # Put 'args' into 'kwargs' with appropriate key.
19
20
  # args, kwargs = put_args_to_kwargs(function_name, *args, **kwargs)
20
- args, kwargs = get_target_function_default_args_and_combine_with_current(function_name, *args, **kwargs)
21
+ args, kwargs = inspect_wrapper.get_target_function_default_args_and_combine_with_current(
22
+ function_name, *args, **kwargs)
21
23
 
22
- print_api(message=f"Writing file: {kwargs['file_path']}", **kwargs)
24
+ print_api.print_api(message=f"Writing file: {kwargs['file_path']}", **kwargs)
25
+
26
+ enable_long_file_path = kwargs.get('enable_long_file_path', False)
27
+ if enable_long_file_path and os.name == 'nt':
28
+ # A simpler string method would be to add '\\?\' to the beginning of the file path.
29
+ # kwargs['file_path'] = rf"\\?\{kwargs['file_path']}"
30
+
31
+ # Enable long file path.
32
+ from ctypes import windll
33
+ # Enable long file path.
34
+ windll.kernel32.SetFileAttributesW(kwargs['file_path'], 0x80)
23
35
 
24
36
  try:
25
37
  with open(kwargs['file_path'], kwargs['file_mode'], encoding=kwargs['encoding']) as output_file:
@@ -31,7 +43,7 @@ def write_file_decorator(function_name):
31
43
  except FileExistsError:
32
44
  message = f"Can't write file: {kwargs['file_path']}\n" \
33
45
  f"File exists, you should enable force/overwrite mode."
34
- print_api(message, error_type=True, logger_method='critical', **kwargs)
46
+ print_api.print_api(message, error_type=True, logger_method='critical', **kwargs)
35
47
 
36
48
  return wrapper_write_file_decorator
37
49
 
@@ -41,12 +53,13 @@ def read_file_decorator(function_name):
41
53
  def wrapper_read_file_decorator(*args, **kwargs):
42
54
  # Put 'args' into 'kwargs' with appropriate key.
43
55
  # args, kwargs = put_args_to_kwargs(function_name, *args, **kwargs)
44
- args, kwargs = get_target_function_default_args_and_combine_with_current(function_name, *args, **kwargs)
56
+ args, kwargs = inspect_wrapper.get_target_function_default_args_and_combine_with_current(
57
+ function_name, *args, **kwargs)
45
58
 
46
59
  continue_loop: bool = True
47
60
  while continue_loop:
48
61
  try:
49
- print_api(message=f"Reading file: {kwargs['file_path']}", **kwargs)
62
+ print_api.print_api(message=f"Reading file: {kwargs['file_path']}", **kwargs)
50
63
  with open(kwargs['file_path'], kwargs['file_mode'], encoding=kwargs['encoding']) as input_file:
51
64
  # Pass the 'output_file' object to kwargs that will pass the object to the executing function.
52
65
  kwargs['file_object'] = input_file
@@ -54,19 +67,19 @@ def read_file_decorator(function_name):
54
67
  return function_name(**kwargs)
55
68
  except FileNotFoundError:
56
69
  message = f"File doesn't exist: {kwargs['file_path']}"
57
- print_api(message, error_type=True, logger_method='critical', **kwargs)
70
+ print_api.print_api(message, error_type=True, logger_method='critical', **kwargs)
58
71
  raise
59
72
  except UnicodeDecodeError as exception_object:
60
73
  if kwargs["encoding"] != 'utf-8':
61
74
  message = f'File decode error, current encoding: {kwargs["encoding"]}. Will try "utf-8".'
62
- print_api(message, logger_method='error', **kwargs)
75
+ print_api.print_api(message, logger_method='error', **kwargs)
63
76
  kwargs["encoding"] = 'utf-8'
64
77
  pass
65
78
  continue
66
79
  else:
67
80
  message = f'File decode error.\n' \
68
81
  f'{exception_object}'
69
- print_api(message, merror_type=True, logger_method='critical', **kwargs)
82
+ print_api.print_api(message, merror_type=True, logger_method='critical', **kwargs)
70
83
  continue_loop = False
71
84
 
72
85
  return wrapper_read_file_decorator
@@ -78,7 +91,7 @@ def write_file(
78
91
  file_path: str,
79
92
  file_mode: str = 'w',
80
93
  encoding: str = None,
81
- convert_list_to_string: bool = False,
94
+ enable_long_file_path: bool = False,
82
95
  file_object=None,
83
96
  **kwargs) -> None:
84
97
  """
@@ -90,20 +103,23 @@ def write_file(
90
103
  Default is 'w'.
91
104
  :param encoding: string, write the file with encoding. Example: 'utf-8'. 'None' is default, since it is default
92
105
  in 'open()' function.
93
- :param convert_list_to_string: Boolean, if True, the list of strings will be converted to one string with '\n'
94
- separator between the lines.
106
+ :param enable_long_file_path: Boolean, by default Windows has a limit of 260 characters for file path. If True,
107
+ the long file path will be enabled, and the limit will be 32,767 characters.
95
108
  :param file_object: file object of the 'open()' function in the decorator. Decorator executes the 'with open()'
96
109
  statement and passes to this function. That's why the default is 'None', since we get it from the decorator.
97
110
  :return:
98
111
  """
99
112
 
100
113
  if isinstance(content, list):
101
- if convert_list_to_string:
102
- content = '\n'.join(content)
103
- else:
104
- file_object.writelines(content)
105
-
106
- if isinstance(content, str):
114
+ for line in content:
115
+ if not line.endswith("\n"):
116
+ file_object.write(line + "\n")
117
+ else:
118
+ file_object.write(line)
119
+ elif isinstance(content, str):
120
+ file_object.write(content)
121
+ # THis will happen if the content is bytes and the file mode is 'wb'.
122
+ elif isinstance(content, bytes) and 'b' in file_mode:
107
123
  file_object.write(content)
108
124
  else:
109
125
  raise TypeError(f"Content type is not supported: {type(content)}")
@@ -133,3 +133,52 @@ def is_dict_json_serializable(
133
133
  raise e
134
134
  else:
135
135
  return False, str(e)
136
+
137
+
138
+ def append_to_json(
139
+ dict_or_list: Union[dict, list],
140
+ json_file_path: str,
141
+ indent=None,
142
+ use_default_indent=False,
143
+ enable_long_file_path=False,
144
+ print_kwargs: dict = None
145
+ ) -> None:
146
+ """
147
+ Append dictionary or list of dictionaries to json file.
148
+
149
+ :param dict_or_list: dictionary or list of dictionaries to append.
150
+ :param json_file_path: full file path to the json file.
151
+ :param indent: integer number of spaces for indentation.
152
+ If 'ident=0' new lines still will be created. The most compact is 'indent=None' (from documentation)
153
+ So, using default as 'None' and not something else.
154
+ :param use_default_indent: boolean. Default indent for 'json' format in many places is '2'. So, if you don't want
155
+ to set 'indent=2', just set this to 'True'.
156
+ :param enable_long_file_path: Boolean, by default Windows has a limit of 260 characters for file path. If True,
157
+ the long file path will be enabled, and the limit will be 32,767 characters.
158
+ :param print_kwargs: dict, the print_api arguments.
159
+ :return:
160
+ """
161
+
162
+ # Read existing data from the file
163
+ try:
164
+ with open(json_file_path, 'r') as f:
165
+ current_json_file = json.load(f)
166
+ except FileNotFoundError:
167
+ current_json_file: list = []
168
+
169
+ # Append the new message to the existing data
170
+ final_json_list_of_dicts: list[dict] = []
171
+ if isinstance(current_json_file, list):
172
+ current_json_file.append(dict_or_list)
173
+ final_json_list_of_dicts = current_json_file
174
+ elif isinstance(current_json_file, dict):
175
+ final_json_list_of_dicts.append(current_json_file)
176
+ final_json_list_of_dicts.append(dict_or_list)
177
+ else:
178
+ error_message = "The current file is neither a list nor a dictionary."
179
+ raise TypeError(error_message)
180
+
181
+ # Write the data back to the file
182
+ write_json_file(
183
+ final_json_list_of_dicts, json_file_path, indent=indent, use_default_indent=use_default_indent,
184
+ enable_long_file_path=enable_long_file_path, **print_kwargs)
@@ -5,11 +5,17 @@ try:
5
5
  import tomllib
6
6
  except ModuleNotFoundError:
7
7
  # This is library from pypi.
8
+ # noinspection PyPackageRequirements
8
9
  import tomli as tomllib
9
10
 
10
11
  from . import file_io
11
12
 
12
13
 
14
+ class TomlValueNotImplementedError(Exception):
15
+ pass
16
+
17
+
18
+ # noinspection PyUnusedLocal
13
19
  @file_io.read_file_decorator
14
20
  def read_toml_file(file_path: str,
15
21
  file_mode: str = 'rb',
@@ -31,6 +37,7 @@ def read_toml_file(file_path: str,
31
37
  return tomllib.load(file_object)
32
38
 
33
39
 
40
+ # noinspection PyUnusedLocal
34
41
  @file_io.write_file_decorator
35
42
  def write_toml_file(
36
43
  toml_content: dict,
@@ -84,3 +91,135 @@ def dumps(toml_dict: dict):
84
91
  toml_string += process_item(key, value)
85
92
 
86
93
  return toml_string
94
+
95
+
96
+ def update_toml_file_with_new_config(
97
+ main_config_file_path: str,
98
+ changes_config_file_path: str = None,
99
+ changes_dict: dict = None,
100
+ new_config_file_path: str = None
101
+ ) -> None:
102
+ """
103
+ Update the old toml config file with the new values from the new toml config file.
104
+ This will update only the changed values.
105
+ If the values from the changes file aren't present in the main config file, they will not be added.
106
+
107
+ :param main_config_file_path: string, path to the main config file that you want to use as the main reference.
108
+ If you provide the 'new_config_file_path', then changes to the 'main_config_file_path' will be written there.
109
+ :param changes_config_file_path: string, the config file path that have the changes.
110
+ Only changed values will be updated to the 'main_config_file_path'.
111
+ :param changes_dict: dict, the dictionary with the changes.
112
+ Instead of providing the 'changes_config_file_path', you can provide only the dictionary with the changes.
113
+ :param new_config_file_path: string, path to the new config file.
114
+ If provided, the changes will be written to this file.
115
+ If not, the changes will be written to the 'main_config_file_path'.
116
+ """
117
+
118
+ if not changes_config_file_path and not changes_dict:
119
+ raise ValueError("You must provide either 'changes_config_file_path' or 'changes_dict'.")
120
+ if changes_config_file_path and changes_dict:
121
+ raise ValueError("You can't provide both 'changes_config_file_path' and 'changes_dict'.")
122
+
123
+ with open(main_config_file_path, 'r') as file:
124
+ main_config_file_text_lines: list = file.readlines()
125
+
126
+ main_config_file_text_lines_backup: list = list(main_config_file_text_lines)
127
+
128
+ # Read the new config file.
129
+ main_config_file_dict: dict = read_toml_file(main_config_file_path)
130
+
131
+ if not changes_dict:
132
+ changes_dict: dict = read_toml_file(changes_config_file_path)
133
+
134
+ # Update the config text lines.
135
+ for category, settings in main_config_file_dict.items():
136
+ if category not in changes_dict:
137
+ continue
138
+
139
+ for key, value in settings.items():
140
+ # If the key is in the old config file, use the old value.
141
+ if key not in changes_dict[category]:
142
+ continue
143
+
144
+ if main_config_file_dict[category][key] != changes_dict[category][key]:
145
+ # Get the line of the current category line.
146
+ current_category_line_index_in_text = None
147
+ for current_category_line_index_in_text, line in enumerate(main_config_file_text_lines):
148
+ if f"[{category}]" in line:
149
+ break
150
+
151
+ # Get the index inside the main config file dictionary of the current category.
152
+ main_config_list_of_keys: list = list(main_config_file_dict.keys())
153
+ current_category_index_in_main_config_dict = main_config_list_of_keys.index(category)
154
+
155
+ try:
156
+ next_category_name = list(
157
+ main_config_file_dict.keys())[current_category_index_in_main_config_dict + 1]
158
+ except IndexError:
159
+ next_category_name = list(main_config_file_dict.keys())[-1]
160
+
161
+ next_category_line_index_in_text = None
162
+ for next_category_line_index_in_text, line in enumerate(main_config_file_text_lines):
163
+ if f"[{next_category_name}]" in line:
164
+ break
165
+
166
+ # In case the current and the next categories are the same and the last in the file.
167
+ if next_category_line_index_in_text == current_category_line_index_in_text:
168
+ next_category_line_index_in_text = len(main_config_file_text_lines)
169
+
170
+ string_to_check_list: list = list()
171
+ if isinstance(value, bool):
172
+ string_to_check_list.append(f"{key} = {str(value).lower()}")
173
+ elif isinstance(value, int):
174
+ string_to_check_list.append(f"{key} = {value}")
175
+ elif isinstance(value, str):
176
+ string_to_check_list.append(f"{key} = '{value}'")
177
+ string_to_check_list.append(f'{key} = "{value}"')
178
+ else:
179
+ raise TomlValueNotImplementedError(f"Value type '{type(value)}' not implemented.")
180
+
181
+ # next_category_line_index_in_text = main_config_file_text_lines.index(f"[{next_category_name}]\n")
182
+ # Find the index of this line in the text file between current category line and
183
+ # the next category line.
184
+ line_index = None
185
+ found_line = False
186
+ for line_index in range(current_category_line_index_in_text, next_category_line_index_in_text):
187
+ if found_line:
188
+ line_index = line_index - 1
189
+ break
190
+ for string_to_check in string_to_check_list:
191
+ if string_to_check in main_config_file_text_lines[line_index]:
192
+ found_line = True
193
+ break
194
+
195
+ if found_line:
196
+ # If there are comments, get only them from the line. Comment will also get the '\n' character.
197
+ # noinspection PyUnboundLocalVariable
198
+ comment = main_config_file_text_lines[line_index].replace(string_to_check, '')
199
+
200
+ object_type = type(changes_dict[category][key])
201
+ if object_type == bool:
202
+ value_string_to_set = str(changes_dict[category][key]).lower()
203
+ elif object_type == str:
204
+ value_string_to_set = f"'{changes_dict[category][key]}'"
205
+ elif object_type == int:
206
+ value_string_to_set = str(changes_dict[category][key])
207
+
208
+ # noinspection PyUnboundLocalVariable
209
+ line_to_set = f"{key} = {value_string_to_set}{comment}"
210
+ # Replace the line with the old value.
211
+ main_config_file_text_lines[line_index] = line_to_set
212
+
213
+ main_config_file_dict[category][key] = changes_dict[category][key]
214
+
215
+ if new_config_file_path:
216
+ file_path_to_write = new_config_file_path
217
+ else:
218
+ file_path_to_write = main_config_file_path
219
+
220
+ if not main_config_file_text_lines == main_config_file_text_lines_backup:
221
+ # Write the final config file.
222
+ with open(file_path_to_write, 'w') as file:
223
+ file.writelines(main_config_file_text_lines)
224
+ else:
225
+ print("No changes to the config file.")