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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
atomicshop/file_io/file_io.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from typing import Union
|
|
2
2
|
import functools
|
|
3
|
+
import os
|
|
3
4
|
|
|
4
|
-
from ..
|
|
5
|
-
from ..
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
94
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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)}")
|
atomicshop/file_io/jsons.py
CHANGED
|
@@ -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)
|
atomicshop/file_io/tomls.py
CHANGED
|
@@ -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.")
|