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/basics/strings.py
CHANGED
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
import argparse
|
|
5
5
|
|
|
6
6
|
from . import lists
|
|
7
|
-
from ..
|
|
7
|
+
from .. import print_api
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def get_nth_character_from_start(input_string: str, nth: int):
|
|
@@ -108,7 +108,12 @@ def is_any_string_from_list_in_string(string_list: list, check_string: str) -> b
|
|
|
108
108
|
return any(test_string in check_string for test_string in string_list)
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
def
|
|
111
|
+
def _match_pattern_against_string(
|
|
112
|
+
pattern: str,
|
|
113
|
+
check_string: str,
|
|
114
|
+
case_insensitive: bool = False,
|
|
115
|
+
prefix_suffix: bool = False
|
|
116
|
+
) -> bool:
|
|
112
117
|
"""
|
|
113
118
|
Function checks the 'pattern' against 'check_string' and returns 'True' if pattern matches and 'False' if not.
|
|
114
119
|
|
|
@@ -121,6 +126,7 @@ def match_pattern_against_string(pattern: str, check_string: str, prefix_suffix:
|
|
|
121
126
|
|
|
122
127
|
:param pattern: string, can include wildcards as '*'.
|
|
123
128
|
:param check_string: string, to check the pattern against.
|
|
129
|
+
:param case_insensitive: boolean, if 'True' will treat the 'pattern' and 'check_string' as case-insensitive.
|
|
124
130
|
:param prefix_suffix: boolean, that sets if the function should return 'True' also for all the cases that wildcard
|
|
125
131
|
in the beginning of the pattern and in the end of the pattern, since the default behavior of regex to return
|
|
126
132
|
'False' on these cases.
|
|
@@ -152,12 +158,16 @@ def match_pattern_against_string(pattern: str, check_string: str, prefix_suffix:
|
|
|
152
158
|
# on complex strings.
|
|
153
159
|
# return fnmatch.fnmatch(check_string, pattern)
|
|
154
160
|
|
|
161
|
+
# Determine the regex flags based on case_insensitive.
|
|
162
|
+
flags = re.IGNORECASE if case_insensitive else 0
|
|
163
|
+
|
|
155
164
|
def search_pattern(function_pattern):
|
|
156
165
|
# Use regex to match the pattern.
|
|
157
|
-
return re.search(fr'{function_pattern}', check_string)
|
|
166
|
+
return re.search(fr'{function_pattern}', check_string, flags)
|
|
158
167
|
|
|
159
168
|
wildcard_str: str = '*'
|
|
160
169
|
wildcard_re: str = '.+'
|
|
170
|
+
# wildcard_re: str = '.*' # Adjusted to '.*' to match zero or more characters
|
|
161
171
|
|
|
162
172
|
# Replace the wildcard string '*' with regex wildcard string '.+'.
|
|
163
173
|
# In regex '.' is a wildcard, but only for 1 character, if you need more than 1 character you should add '+'.
|
|
@@ -204,13 +214,86 @@ def match_pattern_against_string(pattern: str, check_string: str, prefix_suffix:
|
|
|
204
214
|
return False
|
|
205
215
|
|
|
206
216
|
|
|
207
|
-
def
|
|
217
|
+
def match_pattern_against_string(
|
|
218
|
+
pattern: str,
|
|
219
|
+
check_string: str,
|
|
220
|
+
case_insensitive: bool = False,
|
|
221
|
+
prefix_suffix: bool = False
|
|
222
|
+
) -> bool:
|
|
223
|
+
"""
|
|
224
|
+
Function checks the 'pattern' against 'check_string' and returns 'True' if pattern matches and 'False' if not.
|
|
225
|
+
|
|
226
|
+
Example:
|
|
227
|
+
pattern_string = "*ffmpeg*full_build.zip"
|
|
228
|
+
check_string = "https://github.com/GyanD/codexffmpeg/releases/download/5.1.2/ffmpeg-5.1.2-full_build.zip"
|
|
229
|
+
match_pattern_against_string(pattern_string, check_string)
|
|
230
|
+
Result:
|
|
231
|
+
True
|
|
232
|
+
|
|
233
|
+
:param pattern: string, can include wildcards as '*'.
|
|
234
|
+
:param check_string: string, to check the pattern against.
|
|
235
|
+
:param case_insensitive: boolean, if 'True' will treat the 'pattern' and 'check_string' as case-insensitive.
|
|
236
|
+
:param prefix_suffix: boolean, that sets if the function should return 'True' also for all the cases that wildcard
|
|
237
|
+
in the beginning of the pattern and in the end of the pattern, since the default behavior of regex to return
|
|
238
|
+
'False' on these cases.
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
pattern: *test
|
|
242
|
+
check_string: testblabla
|
|
243
|
+
Default regex behavior will return 'False' with 'prefix_suffix' switch set to 'True',
|
|
244
|
+
this case will return 'True'. Same will go for:
|
|
245
|
+
pattern: test*
|
|
246
|
+
check_string: blablatest
|
|
247
|
+
|
|
248
|
+
Why this is good?
|
|
249
|
+
Let's say you have a python script 'example.py' and you want to find all the executed command lines,
|
|
250
|
+
and you want to make sure that this was executed by 'python'. Your python is installed
|
|
251
|
+
in 'c:\\Python310\\python.exe', and you want to match all the possible patterns of 'python' and 'example.py'.
|
|
252
|
+
Pattern:
|
|
253
|
+
*python*example.py
|
|
254
|
+
You want to match 'True' for the next cases:
|
|
255
|
+
python example.py
|
|
256
|
+
c:\\Python310\\python.exe example.py
|
|
257
|
+
|
|
258
|
+
Default regex behavior is to return 'False' on 'python example.py'.
|
|
259
|
+
|
|
260
|
+
:return: boolean.
|
|
261
|
+
"""
|
|
262
|
+
# Determine the regex flags based on case_insensitive.
|
|
263
|
+
flags = re.IGNORECASE if case_insensitive else 0
|
|
264
|
+
|
|
265
|
+
# Escape the pattern for regex, then replace '*' with '.*' to match any characters.
|
|
266
|
+
escaped_pattern = re.escape(pattern).replace(r'\*', '.*')
|
|
267
|
+
|
|
268
|
+
# Adjust the pattern to match from the start and/or end based on prefix_suffix.
|
|
269
|
+
if prefix_suffix:
|
|
270
|
+
if not pattern.startswith('*'):
|
|
271
|
+
escaped_pattern = '.*' + escaped_pattern
|
|
272
|
+
if not pattern.endswith('*'):
|
|
273
|
+
escaped_pattern = escaped_pattern + '.*'
|
|
274
|
+
else:
|
|
275
|
+
escaped_pattern = '^' + escaped_pattern + '$'
|
|
276
|
+
|
|
277
|
+
# Compile the regex pattern with the appropriate flags.
|
|
278
|
+
regex_pattern = re.compile(escaped_pattern, flags)
|
|
279
|
+
|
|
280
|
+
# Perform the search and return the result.
|
|
281
|
+
return bool(regex_pattern.search(check_string))
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def match_list_of_patterns_against_string(
|
|
285
|
+
patterns: list,
|
|
286
|
+
check_string: str,
|
|
287
|
+
case_insensitive: bool = False,
|
|
288
|
+
prefix_suffix: bool = False
|
|
289
|
+
) -> bool:
|
|
208
290
|
"""
|
|
209
291
|
Function checks each pattern in 'patterns' list against 'check_string' and returns 'True' if any pattern matches
|
|
210
292
|
and 'False' if not.
|
|
211
293
|
|
|
212
294
|
:param patterns: list, of string patterns to check against. May include wildcards.
|
|
213
295
|
:param check_string: string, to check the pattern against.
|
|
296
|
+
:param case_insensitive: boolean, if 'True' will treat the 'pattern' and 'check_string' as case-insensitive.
|
|
214
297
|
:param prefix_suffix: boolean, that sets if the function should return 'True' also for all the cases that wildcard
|
|
215
298
|
in the beginning of the pattern and in the end of the pattern, since the default behavior of regex to return
|
|
216
299
|
'False' on these cases.
|
|
@@ -221,7 +304,8 @@ def match_list_of_patterns_against_string(patterns: list, check_string: str, pre
|
|
|
221
304
|
"""
|
|
222
305
|
|
|
223
306
|
for pattern in patterns:
|
|
224
|
-
if match_pattern_against_string(
|
|
307
|
+
if match_pattern_against_string(
|
|
308
|
+
pattern, check_string, case_insensitive=case_insensitive, prefix_suffix=prefix_suffix):
|
|
225
309
|
return True
|
|
226
310
|
|
|
227
311
|
return False
|
|
@@ -339,7 +423,7 @@ def replace_words_with_values_from_dict(
|
|
|
339
423
|
|
|
340
424
|
|
|
341
425
|
def replace_strings_with_values_from_dict(string_to_replace: str, dictionary: dict) -> str:
|
|
342
|
-
"""
|
|
426
|
+
r"""
|
|
343
427
|
Function replaces strings, which are keys with values from dictionary.
|
|
344
428
|
|
|
345
429
|
:param string_to_replace: string, to replace words in.
|
|
@@ -453,7 +537,7 @@ def replace_string_in_file(
|
|
|
453
537
|
file.writelines(lines)
|
|
454
538
|
|
|
455
539
|
# Output the relevant line numbers
|
|
456
|
-
print_api(f"Target string found on the following lines: {changed_lines}", **(print_kwargs or {}))
|
|
540
|
+
print_api.print_api(f"Target string found on the following lines: {changed_lines}", **(print_kwargs or {}))
|
|
457
541
|
return changed_lines
|
|
458
542
|
|
|
459
543
|
|
|
@@ -487,3 +571,72 @@ def replace_string_in_file_main_argparse():
|
|
|
487
571
|
new_string=args.new_string,
|
|
488
572
|
find_only=args.find_only
|
|
489
573
|
)
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def _replace_string_in_variable():
|
|
577
|
+
"""
|
|
578
|
+
Replace string in a string variable, but do it by a meta variable inside the string.
|
|
579
|
+
This is just an example, using the 'Template' class from the 'string' module is a better way to do it.
|
|
580
|
+
"""
|
|
581
|
+
|
|
582
|
+
from string import Template
|
|
583
|
+
import os
|
|
584
|
+
import tempfile
|
|
585
|
+
import subprocess
|
|
586
|
+
|
|
587
|
+
get_docker_url: str = "https://get.docker.com"
|
|
588
|
+
docker_proxy_image_name: str = "rpardini/docker-registry-proxy:0.6.5"
|
|
589
|
+
preparation_output_dir: str = str(Path(__file__).parent / "offline-bundle")
|
|
590
|
+
|
|
591
|
+
class BashTemplate(Template):
|
|
592
|
+
# Anything that is not '$', which is a default delimiter in Template class, but also used in bash scripts.
|
|
593
|
+
# The below symbol can be printed from keyboard by holding 'Alt' and typing '0167' on the numeric keypad.
|
|
594
|
+
delimiter = '§'
|
|
595
|
+
|
|
596
|
+
bash_tmpl = BashTemplate(r"""#!/usr/bin/env bash
|
|
597
|
+
#
|
|
598
|
+
set -Eeuo pipefail
|
|
599
|
+
|
|
600
|
+
die() { echo "ERROR: $*" >&2; exit 1; }
|
|
601
|
+
need_root() { [[ $EUID -eq 0 ]] || die "Run as root (use sudo)"; }
|
|
602
|
+
need_cmd() {
|
|
603
|
+
local cmd=$1
|
|
604
|
+
local pkg=${2:-$1} # default package == command
|
|
605
|
+
if ! command -v "$cmd" &>/dev/null; then
|
|
606
|
+
echo "[*] $cmd not found – installing $pkg ..."
|
|
607
|
+
apt-get update -qq
|
|
608
|
+
DEBIAN_FRONTEND=noninteractive \
|
|
609
|
+
apt-get install -y --no-install-recommends "$pkg" || \
|
|
610
|
+
die "Unable to install required package: $pkg"
|
|
611
|
+
fi
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
need_root
|
|
615
|
+
need_cmd curl # binary and pkg are both “curl”
|
|
616
|
+
need_cmd gpg # → apt-get install gpg
|
|
617
|
+
|
|
618
|
+
DRY_LOG=$(curl -fsSL "§url" | bash -s -- --dry-run)
|
|
619
|
+
IMAGE="§proxyimage"
|
|
620
|
+
ARCHIVE="$OUTDIR/registry-proxy-image.tar.gz"
|
|
621
|
+
zip -r "§output_zip" "$OUTDIR"
|
|
622
|
+
""")
|
|
623
|
+
|
|
624
|
+
# Substitute the variables in the bash script template.
|
|
625
|
+
bash_script = bash_tmpl.substitute(
|
|
626
|
+
url=get_docker_url, proxyimage=docker_proxy_image_name, output_zip=preparation_output_dir)
|
|
627
|
+
|
|
628
|
+
# Write it to a secure temporary file.
|
|
629
|
+
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.sh') as f:
|
|
630
|
+
f.write(bash_script)
|
|
631
|
+
temp_path = f.name
|
|
632
|
+
os.chmod(temp_path, 0o755) # make it executable
|
|
633
|
+
|
|
634
|
+
# Decide where the bundle should land (optional argument to the script).
|
|
635
|
+
cmd = ["sudo", temp_path, preparation_output_dir] # use sudo because the script demands root
|
|
636
|
+
|
|
637
|
+
# Run it and stream output live.
|
|
638
|
+
try:
|
|
639
|
+
subprocess.run(cmd, check=True)
|
|
640
|
+
finally:
|
|
641
|
+
# 5. Clean up the temp file unless you want to inspect it.
|
|
642
|
+
os.remove(temp_path)
|
atomicshop/basics/threads.py
CHANGED
|
@@ -18,6 +18,20 @@ def current_thread_id():
|
|
|
18
18
|
return thread_id
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
def get_current_thread_name():
|
|
22
|
+
return threading.current_thread().name
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def set_current_thread_name(name: str):
|
|
26
|
+
threading.current_thread().name = name
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def set_current_thread_name_by_process_name():
|
|
30
|
+
import multiprocessing
|
|
31
|
+
current_process_name = multiprocessing.current_process().name
|
|
32
|
+
threading.current_thread().name = current_process_name
|
|
33
|
+
|
|
34
|
+
|
|
21
35
|
def get_number_of_active_threads():
|
|
22
36
|
return threading.active_count()
|
|
23
37
|
|
atomicshop/basics/tracebacks.py
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
|
-
# v1.0.0 - 02.04.2023 16:20
|
|
2
1
|
import traceback
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
def get_as_string(
|
|
4
|
+
def get_as_string(
|
|
5
|
+
exc: BaseException = None,
|
|
6
|
+
one_line: bool = False,
|
|
7
|
+
replace_end: str = str()
|
|
8
|
+
) -> str:
|
|
6
9
|
"""
|
|
7
10
|
Returns traceback as string.
|
|
8
11
|
|
|
12
|
+
:param exc: Exception to get traceback from. If 'None', current exception will be used.
|
|
9
13
|
:param one_line: If 'True', traceback will be returned as one line.
|
|
10
14
|
:param replace_end: If 'one_line' is 'True', this string will be used to replace '\n' in traceback.
|
|
11
15
|
:return: Traceback as string.
|
|
12
16
|
"""
|
|
13
17
|
|
|
18
|
+
if exc is None:
|
|
19
|
+
stringed_exception: str = traceback.format_exc()
|
|
20
|
+
else:
|
|
21
|
+
stringed_exception: str = ''.join(traceback.TracebackException.from_exception(exc).format())
|
|
22
|
+
|
|
14
23
|
if not one_line:
|
|
15
|
-
return
|
|
24
|
+
return stringed_exception
|
|
16
25
|
else:
|
|
17
|
-
return
|
|
26
|
+
return stringed_exception.replace('\n', replace_end)
|
atomicshop/certificates.py
CHANGED
|
@@ -5,9 +5,10 @@ https://oidref.com/1.3.6.1.5.5.7.3.1
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
import ssl
|
|
8
|
+
from typing import Literal, Any
|
|
8
9
|
|
|
9
10
|
from .wrappers import cryptographyw
|
|
10
|
-
from .
|
|
11
|
+
from .wrappers.pywin32w import cert_store
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
# Valid for 3 years from now
|
|
@@ -16,72 +17,172 @@ from .print_api import print_api
|
|
|
16
17
|
SECONDS_NOT_AFTER_3_YEARS = 3 * 365 * 24 * 60 * 60
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def
|
|
20
|
+
def get_pem_certificate_from_string(certificate: str) -> str:
|
|
20
21
|
"""
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
:param certificate:
|
|
24
|
-
:
|
|
25
|
-
The problem that the issuer common name is not unique, so it can be installed multiple times.
|
|
26
|
-
:param thumbprint_only: bool, if True, will check only by the certificate thumbprint is installed in the store.
|
|
27
|
-
The problem that searching by the thumbprint will not tell you if there are multiple certificates with the same
|
|
28
|
-
issuer name.
|
|
29
|
-
:return: bool, True if certificate is installed, False if not.
|
|
22
|
+
Some PEM certificates can contain a private key. This function will return only the certificate part.
|
|
23
|
+
|
|
24
|
+
:param certificate: string, PEM certificate.
|
|
25
|
+
:return: string, certificate part.
|
|
30
26
|
"""
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
certificate_lines = certificate.split('\n')
|
|
29
|
+
certificate_part = ''
|
|
30
|
+
start = False
|
|
31
|
+
for line in certificate_lines:
|
|
32
|
+
if 'BEGIN CERTIFICATE' in line:
|
|
33
|
+
start = True
|
|
34
|
+
if start:
|
|
35
|
+
certificate_part += line + '\n'
|
|
36
|
+
if 'END CERTIFICATE' in line:
|
|
37
|
+
break
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
for cert, encoding, trust in ssl.enum_certificates("ROOT"):
|
|
40
|
-
store_certificate = cryptographyw.convert_object_to_x509(cert)
|
|
41
|
-
store_issuer_common_name: str = cryptographyw.get_issuer_common_name_from_x509(store_certificate)
|
|
42
|
-
store_thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(store_certificate)
|
|
43
|
-
|
|
44
|
-
if issuer_only:
|
|
45
|
-
if store_issuer_common_name == issuer_common_name:
|
|
46
|
-
return True, certificate
|
|
47
|
-
elif thumbprint_only:
|
|
48
|
-
if store_thumbprint == thumbprint:
|
|
49
|
-
return True, certificate
|
|
50
|
-
elif not issuer_only and not thumbprint_only:
|
|
51
|
-
if store_thumbprint == thumbprint and store_issuer_common_name == issuer_common_name:
|
|
52
|
-
return True, certificate
|
|
39
|
+
return certificate_part
|
|
53
40
|
|
|
54
41
|
|
|
55
|
-
def
|
|
42
|
+
def write_crt_certificate_file_in_pem_format_from_pem_file(
|
|
43
|
+
pem_file_path: str,
|
|
44
|
+
crt_file_path: str
|
|
45
|
+
):
|
|
56
46
|
"""
|
|
57
|
-
The function will
|
|
47
|
+
The function will read the PEM certificate file and write it to the CRT file in PEM format.
|
|
48
|
+
The function is used to convert the PEM certificate file to the CRT file.
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
Basically the point here is that the CRT file is the same as the PEM file, but the extension is different,
|
|
51
|
+
and it doesn't support integrated private key.
|
|
61
52
|
|
|
62
|
-
:
|
|
53
|
+
:param pem_file_path: string, path to the PEM certificate file.
|
|
54
|
+
:param crt_file_path: string, path to the CRT certificate file.
|
|
63
55
|
"""
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
with open(pem_file_path, 'r') as f:
|
|
58
|
+
certificate_string = f.read()
|
|
67
59
|
|
|
68
|
-
|
|
60
|
+
certificate_pem = get_pem_certificate_from_string(certificate_string)
|
|
61
|
+
|
|
62
|
+
with open(crt_file_path, 'w') as f:
|
|
63
|
+
f.write(certificate_pem)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def is_certificate_in_store(
|
|
67
|
+
certificate: Any = None,
|
|
68
|
+
by_cert_issuer: bool = True,
|
|
69
|
+
by_cert_thumbprint: bool = True,
|
|
70
|
+
issuer_name: str = None,
|
|
71
|
+
store_location: str = "ROOT",
|
|
72
|
+
print_kwargs: dict = None
|
|
73
|
+
) -> tuple[bool, list]:
|
|
74
|
+
"""
|
|
75
|
+
The function will check if the CA certificate is installed in the Windows certificate Trusted Root store.
|
|
76
|
+
NO ADMIN RIGHTS NEEDED.
|
|
77
|
+
|
|
78
|
+
:param certificate: x509 object, certificate to check. You can search by certificate or by issuer name.
|
|
79
|
+
Supported types:
|
|
80
|
+
string that is path to file will be imported as bytes object abd converted to x509.Certificate
|
|
81
|
+
After check if it's PEM or DER format.
|
|
82
|
+
string that is PEM certificate will be converted to bytes, then x509.Certificate
|
|
83
|
+
bytes of PEM or DER will be converted to x509.Certificate.
|
|
84
|
+
x509.Certificate will be returned as is.
|
|
85
|
+
:param by_cert_issuer: bool, if True, will check only by the certificate issuer common name is installed in the store.
|
|
86
|
+
The problem if the search will be by issuer alone, that the issuer common name is not unique,
|
|
87
|
+
so it can be installed multiple times.
|
|
88
|
+
:param by_cert_thumbprint: bool, if True, will check only by the certificate thumbprint is installed in the store.
|
|
89
|
+
The problem that searching by the thumbprint alone will not tell you if there are multiple
|
|
90
|
+
certificates with the same issuer name.
|
|
91
|
+
:param issuer_name: string, issuer name to search for. You can search by certificate or by issuer name.
|
|
92
|
+
:param store_location: string, store location to search in. Default is "ROOT".
|
|
93
|
+
:param print_kwargs: dict, print_api kwargs.
|
|
94
|
+
:return: tuple(bool - True if certificate is installed and False if not, list of certificates found)
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
if not by_cert_issuer and not by_cert_thumbprint:
|
|
98
|
+
raise ValueError('At least one of the parameters "by_issuer" or "by_thumbprint" must be True.')
|
|
99
|
+
|
|
100
|
+
if not certificate and not issuer_name:
|
|
101
|
+
raise ValueError('At least one of the parameters "certificate" or "issuer_name" must be provided.')
|
|
102
|
+
elif certificate and issuer_name:
|
|
103
|
+
raise ValueError('Only one of the parameters "certificate" or "issuer_name" must be provided.')
|
|
104
|
+
|
|
105
|
+
if certificate:
|
|
106
|
+
# Make sure the certificate is x509.Certificate object.
|
|
107
|
+
certificate_x509 = cryptographyw.convert_object_to_x509(certificate, print_kwargs=print_kwargs)
|
|
108
|
+
# Get the certificate thumbprint.
|
|
109
|
+
provided_thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(certificate_x509)
|
|
110
|
+
provided_issuer_common_name: str = cryptographyw.get_issuer_common_name_from_x509(certificate_x509)
|
|
111
|
+
elif issuer_name:
|
|
112
|
+
provided_thumbprint = None
|
|
113
|
+
provided_issuer_common_name = issuer_name
|
|
114
|
+
else:
|
|
115
|
+
raise ValueError('At least one of the parameters "certificate" or "issuer_name" must be provided.')
|
|
69
116
|
|
|
70
|
-
|
|
117
|
+
# Iterate over all certificates in the store specifically in the ROOT.
|
|
118
|
+
# for store in ["CA", "ROOT", "MY"]:
|
|
119
|
+
result_found_list: list = []
|
|
120
|
+
found: bool = False
|
|
121
|
+
for cert, encoding, trust in ssl.enum_certificates(store_location):
|
|
122
|
+
# Some certificates in the store can have zero or negative serial number.
|
|
123
|
+
# We will skip them, since they're deprecated by the cryptography library.
|
|
71
124
|
store_certificate = cryptographyw.convert_object_to_x509(cert)
|
|
125
|
+
if not store_certificate:
|
|
126
|
+
continue
|
|
127
|
+
|
|
72
128
|
store_issuer_common_name: str = cryptographyw.get_issuer_common_name_from_x509(store_certificate)
|
|
129
|
+
store_thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(store_certificate)
|
|
73
130
|
|
|
74
|
-
if
|
|
75
|
-
|
|
131
|
+
if certificate:
|
|
132
|
+
if by_cert_issuer and not by_cert_thumbprint:
|
|
133
|
+
if store_issuer_common_name == provided_issuer_common_name:
|
|
134
|
+
result_found_list.append(store_certificate)
|
|
135
|
+
found = True
|
|
136
|
+
elif by_cert_thumbprint and not by_cert_issuer:
|
|
137
|
+
if store_thumbprint == provided_thumbprint:
|
|
138
|
+
result_found_list.append(store_certificate)
|
|
139
|
+
found = True
|
|
140
|
+
elif by_cert_issuer and by_cert_thumbprint:
|
|
141
|
+
if store_thumbprint == provided_thumbprint and store_issuer_common_name == provided_issuer_common_name:
|
|
142
|
+
result_found_list.append(store_certificate)
|
|
143
|
+
found = True
|
|
144
|
+
elif issuer_name:
|
|
145
|
+
if store_issuer_common_name == provided_issuer_common_name:
|
|
146
|
+
result_found_list.append(store_certificate)
|
|
147
|
+
found = True
|
|
148
|
+
|
|
149
|
+
return found, result_found_list
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def delete_certificate_by_issuer_name(
|
|
153
|
+
issuer_name: str,
|
|
154
|
+
store_location: Literal[
|
|
155
|
+
"ROOT",
|
|
156
|
+
"CA",
|
|
157
|
+
"MY"] = "ROOT",
|
|
158
|
+
print_kwargs: dict = None
|
|
159
|
+
):
|
|
160
|
+
"""
|
|
161
|
+
NEED ADMIN RIGHTS.
|
|
162
|
+
The function will remove all certificates with the specified issuer name.
|
|
163
|
+
There can be several certificates with this name.
|
|
76
164
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
165
|
+
:param issuer_name: string, issuer name to search for.
|
|
166
|
+
:param store_location: string, store location to search in. Default is "ROOT".
|
|
167
|
+
:param print_kwargs: dict, print_api kwargs.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
cert_store.delete_certificate_by_issuer_name(issuer_name, store_location, print_kwargs)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def install_certificate_file(
|
|
174
|
+
file_path: str,
|
|
175
|
+
store_location: Literal[
|
|
176
|
+
"ROOT", "CA", "MY"] = "ROOT",
|
|
177
|
+
print_kwargs: dict = None
|
|
178
|
+
):
|
|
179
|
+
"""
|
|
180
|
+
The function will install the certificate from the file to the specified store location.
|
|
181
|
+
NEED ADMIN RIGHTS.
|
|
182
|
+
|
|
183
|
+
:param file_path: string, full file path to the certificate file.
|
|
184
|
+
:param store_location: string, store location to install the certificate. Default is "ROOT".
|
|
185
|
+
:param print_kwargs: dict, print_api kwargs.
|
|
186
|
+
"""
|
|
86
187
|
|
|
87
|
-
|
|
188
|
+
cert_store.install_certificate_file(file_path, store_location, print_kwargs)
|
atomicshop/config_init.py
CHANGED
|
@@ -2,13 +2,17 @@ import os
|
|
|
2
2
|
|
|
3
3
|
from .file_io import tomls
|
|
4
4
|
from . import filesystem
|
|
5
|
-
from .
|
|
5
|
+
from . import print_api
|
|
6
6
|
|
|
7
7
|
CONFIG_FILE_NAME = 'config.toml'
|
|
8
8
|
CONFIG: dict = dict()
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def get_config(
|
|
11
|
+
def get_config(
|
|
12
|
+
script_directory: str = None,
|
|
13
|
+
config_file_name: str = CONFIG_FILE_NAME,
|
|
14
|
+
print_kwargs: dict = None
|
|
15
|
+
) -> dict:
|
|
12
16
|
"""
|
|
13
17
|
Get the config file content.
|
|
14
18
|
|
|
@@ -16,6 +20,7 @@ def get_config(script_directory: str = None, config_file_name: str = CONFIG_FILE
|
|
|
16
20
|
get the working directory instead.
|
|
17
21
|
:param config_file_name: string, name of the config file. Default is 'config.toml' as specified in the constant:
|
|
18
22
|
'CONFIG_FILE_NAME'.
|
|
23
|
+
:param print_kwargs: dict, additional arguments to pass to the print function. Default is None.
|
|
19
24
|
:return: dict.
|
|
20
25
|
"""
|
|
21
26
|
|
|
@@ -25,7 +30,7 @@ def get_config(script_directory: str = None, config_file_name: str = CONFIG_FILE
|
|
|
25
30
|
if not script_directory:
|
|
26
31
|
script_directory = filesystem.get_working_directory()
|
|
27
32
|
|
|
28
|
-
CONFIG = tomls.read_toml_file(f'{script_directory}{os.sep}{config_file_name}')
|
|
33
|
+
CONFIG = tomls.read_toml_file(f'{script_directory}{os.sep}{config_file_name}', **(print_kwargs or {}))
|
|
29
34
|
return CONFIG
|
|
30
35
|
|
|
31
36
|
|
|
@@ -45,7 +50,7 @@ def write_config(
|
|
|
45
50
|
'CONFIG_FILE_NAME'.
|
|
46
51
|
:param print_message: boolean, if True, the function will print the message about the created config file.
|
|
47
52
|
Also, it will wait for the user to press Enter to exit the script.
|
|
48
|
-
If False, the function will not print anything and will not exit
|
|
53
|
+
If False, the function will not print anything and will not exit.
|
|
49
54
|
:return:
|
|
50
55
|
"""
|
|
51
56
|
|
|
@@ -58,11 +63,11 @@ def write_config(
|
|
|
58
63
|
|
|
59
64
|
config_file_path = f'{script_directory}{os.sep}{config_file_name}'
|
|
60
65
|
|
|
61
|
-
if not filesystem.
|
|
66
|
+
if not filesystem.is_file_exists(config_file_path):
|
|
62
67
|
tomls.write_toml_file(config, f'{script_directory}{os.sep}{config_file_name}')
|
|
63
68
|
|
|
64
69
|
if print_message:
|
|
65
|
-
print_api(f"Created config file: {config_file_path}", color="yellow")
|
|
66
|
-
print_api(f"You need to fill it with details.", color="yellow")
|
|
70
|
+
print_api.print_api(f"Created config file: {config_file_path}", color="yellow")
|
|
71
|
+
print_api.print_api(f"You need to fill it with details.", color="yellow")
|
|
67
72
|
input("Press Enter to exit.")
|
|
68
73
|
exit()
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
# v1.0.2 - 26.03.2023 20:40
|
|
2
1
|
import sys
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
def query_positive_negative(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
def query_positive_negative(
|
|
5
|
+
question_string: str,
|
|
6
|
+
add_first_values_to_question: bool = True,
|
|
7
|
+
positive_answers: list = None,
|
|
8
|
+
negative_answers: list = None
|
|
9
|
+
) -> bool:
|
|
9
10
|
"""
|
|
10
11
|
Ask for "yes" / "no" input to a question that is passed as a "question_string".
|
|
11
12
|
Returns 'True' for 'positive' answers and 'False' for 'negative' answers.
|
|
@@ -31,10 +32,8 @@ def query_positive_negative(question_string: str,
|
|
|
31
32
|
if add_first_values_to_question:
|
|
32
33
|
question_string = f'{question_string} [{positive_answers[0]}/{negative_answers[0]}]'
|
|
33
34
|
|
|
34
|
-
# Defining variable as False for While loop to run
|
|
35
|
-
right_answer = False
|
|
36
35
|
# As long as "right_answer" is False the loop will execute again
|
|
37
|
-
while
|
|
36
|
+
while True:
|
|
38
37
|
# Print the passed question
|
|
39
38
|
print(question_string)
|
|
40
39
|
# Get the input from the console in lowercase
|
|
@@ -42,22 +41,16 @@ def query_positive_negative(question_string: str,
|
|
|
42
41
|
|
|
43
42
|
# If the gathered value is in "Yes" answers array
|
|
44
43
|
if choice in positive_answers:
|
|
45
|
-
# "right_answer" variable is True, so the loop will not execute again
|
|
46
|
-
right_answer = True
|
|
47
44
|
# Function will return True
|
|
48
45
|
return True
|
|
49
46
|
# Else If the gathered value is in "No" answers array
|
|
50
47
|
elif choice in negative_answers:
|
|
51
|
-
# "right_answer" variable is True, so the loop will not execute again
|
|
52
|
-
right_answer = True
|
|
53
48
|
# Function will return False
|
|
54
49
|
return False
|
|
55
50
|
# If the gathered input is not in the arrays
|
|
56
51
|
else:
|
|
57
52
|
# Then output to console the message
|
|
58
53
|
print("Please respond with either:", positive_answers, negative_answers)
|
|
59
|
-
# "right_answer" variable stays False, so the loop will execute again
|
|
60
|
-
right_answer = False
|
|
61
54
|
|
|
62
55
|
|
|
63
56
|
def do_you_want_to_continue_yn(message: str) -> None:
|
atomicshop/consoles.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import msvcrt
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# === WINDOWS ONLY FUNCTIONS ===========================================================================================
|
|
5
|
+
def wait_any_key(prompt="Press any key to continue..."):
|
|
6
|
+
print(prompt, end="", flush=True)
|
|
7
|
+
msvcrt.getch() # waits for one key press (no Enter needed)
|
|
8
|
+
print() # move to next line
|
|
9
|
+
# === EOF WINDOWS ONLY FUNCTIONS =======================================================================================
|