From 6c2637ad340d86060be52ed3dcf4fa6bbdbe595a Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 14 Jul 2025 15:57:19 +0900 Subject: [PATCH] Settings loader update with basic email check, and on check abort if not valid In the settings checker, if a regex_clean is set as None then we will abort the script with error if the regex is not matching Add regex check for email basic Also add a regex_constants list with regex entries (not compiled and compiled) --- pyproject.toml | 3 +- .../regex_compiled_constants.py | 10 ++++++ .../config_handling/settings_loader.py | 31 ++++++++++++++----- .../settings_loader_check.py | 21 ++++++++++--- test-run/config_handling/config/settings.ini | 2 ++ test-run/config_handling/settings_loader.py | 23 +++++++++++++- test-run/logging_handling/log.py | 3 +- uv.lock | 2 +- 8 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 src/corelibs/check_handling/regex_compiled_constants.py diff --git a/pyproject.toml b/pyproject.toml index b8cc9bb..5ad2a5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,8 @@ notes-rgx = '(FIXME|TODO)(\((TTD-|#)\[0-9]+\))' [tool.flake8] max-line-length = 120 ignore = [ - "E741" # ignore ambigious variable name + "E741", # ignore ambigious variable name + "W504" # Line break occurred after a binary operator [wrong triggered by "or" in if] ] [tool.pylint.MASTER] # this is for the tests/etc folders diff --git a/src/corelibs/check_handling/regex_compiled_constants.py b/src/corelibs/check_handling/regex_compiled_constants.py new file mode 100644 index 0000000..33aa8f0 --- /dev/null +++ b/src/corelibs/check_handling/regex_compiled_constants.py @@ -0,0 +1,10 @@ +""" +List of regex compiled strings that can be used +""" + +EMAIL_REGEX_BASIC = r""" +^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63} +@(?!-)[A-Za-z0-9-]{1,63}(? dict[str, str]: """ neutral settings loader @@ -184,6 +186,8 @@ class SettingsLoader: settings[config_id][entry] = self.__check_settings( check, entry, settings[config_id][entry] ) + if self._check_settings_abort is True: + error = True elif check.startswith("matching:"): checks = check.replace("matching:", "").split("|") if __result := is_list_in_list(convert_to_list(settings[config_id][entry]), list(checks)): @@ -253,6 +257,7 @@ class SettingsLoader: return settings[config_id] + # MARK: build from/to/requal logic def __build_from_to_equal( self, entry: str, check: str, convert_to_int: bool = False ) -> Tuple[float | None, float | None, float | None]: @@ -306,6 +311,7 @@ class SettingsLoader: __equal ) + # MARK: length/range validation def __length_range_validate( self, entry: str, @@ -347,6 +353,7 @@ class SettingsLoader: continue return valid + # MARK: load config file data from file def __load_config_file(self) -> configparser.ConfigParser | None: """ load and parse the config file @@ -358,13 +365,14 @@ class SettingsLoader: return config return None + # MARK: regex clean up one def __clean_invalid_setting( self, entry: str, validate: str, value: str, regex: str, - regex_clean: str, + regex_clean: str | None, replace: str = "", print_error: bool = True, ) -> str: @@ -380,18 +388,24 @@ class SettingsLoader: replace (str): replace with character. Defaults to '' print_error (bool): print the error message. Defaults to True """ - check = re.compile(regex) - clean = re.compile(regex_clean) + check = re.compile(regex, re.VERBOSE) + clean: re.Pattern[str] | None = None + if regex_clean is not None: + clean = re.compile(regex_clean, re.VERBOSE) if not check.search(value): self.__print( f"[!] Invalid content for '{entry}' with check '{validate}' and data: {value}", 'ERROR', print_error ) - # clean up - return clean.sub(replace, value) + # clean up if clean up is not none, else return EMPTY string + if clean is not None: + return clean.sub(replace, value) + self._check_settings_abort = True + return '' # else return as is return value + # MARK: check settings, regx def __check_settings( self, check: str, entry: str, setting_value: list[str] | str @@ -439,6 +453,7 @@ class SettingsLoader: # return data return setting_value + # MARK: check arguments, for config file load fail def __check_arguments(self, arguments: dict[str, list[str]], all_set: bool = False) -> bool: """ check if ast least one argument is set @@ -468,6 +483,7 @@ class SettingsLoader: return has_argument + # MARK: get argument from args dict def __get_arg(self, entry: str) -> Any: """ check if an argument entry xists, if None -> returns None else value of argument @@ -482,6 +498,7 @@ class SettingsLoader: return None return self.args.get(entry) + # MARK: error print def __print(self, msg: str, level: str, print_error: bool = True, raise_exception: bool = False): """ print out error, if Log class is set then print to log instead diff --git a/src/corelibs/config_handling/settings_loader_handling/settings_loader_check.py b/src/corelibs/config_handling/settings_loader_handling/settings_loader_check.py index 020715f..bc9eea7 100644 --- a/src/corelibs/config_handling/settings_loader_handling/settings_loader_check.py +++ b/src/corelibs/config_handling/settings_loader_handling/settings_loader_check.py @@ -3,12 +3,15 @@ Class of checks that can be run on value entries """ from typing import TypedDict +from corelibs.check_handling.regex_compiled_constants import EMAIL_REGEX_BASIC class SettingsLoaderCheckValue(TypedDict): """Settings check entries""" + regex: str - regex_clean: str + # if None, then on error we exit, eles we clean up data + regex_clean: str | None replace: str @@ -16,29 +19,37 @@ class SettingsLoaderCheck: """ check: or check:list+ """ + CHECK_SETTINGS: dict[str, SettingsLoaderCheckValue] = { "int": { "regex": r"^[0-9]+$", "regex_clean": r"[^0-9]", - "replace": "" + "replace": "", }, "string.alphanumeric": { "regex": r"^[a-zA-Z0-9]+$", "regex_clean": r"[^a-zA-Z0-9]", - "replace": "" + "replace": "", }, "string.alphanumeric.lower.dash": { "regex": r"^[a-z0-9-]+$", "regex_clean": r"[^a-z0-9-]", - "replace": "" + "replace": "", }, # A-Z a-z 0-9 _ - . ONLY # This one does not remove, but replaces with _ "string.alphanumeric.extended.replace": { "regex": r"^[_.a-zA-Z0-9-]+$", "regex_clean": r"[^_.a-zA-Z0-9-]", - "replace": "_" + "replace": "_", + }, + # This does a baisc email check, only alphanumeric with special characters + "string.email.basic": { + "regex": EMAIL_REGEX_BASIC, + "regex_clean": None, + "replace": "", }, } + # __END__ diff --git a/test-run/config_handling/config/settings.ini b/test-run/config_handling/config/settings.ini index 396f181..6558bb9 100644 --- a/test-run/config_handling/config/settings.ini +++ b/test-run/config_handling/config/settings.ini @@ -21,3 +21,5 @@ match_source_list=foo,bar element_a=Static energy element_b=123.5 element_c=True +email=foo@bar.com,other+bar-fee@domain-com.cp +email_bad=@bar.com diff --git a/test-run/config_handling/settings_loader.py b/test-run/config_handling/settings_loader.py index 0fa5348..a3e927b 100644 --- a/test-run/config_handling/settings_loader.py +++ b/test-run/config_handling/settings_loader.py @@ -68,7 +68,28 @@ def main(): "match_source_list": ["split:,", "in:match_target_list"], } ) - print(f"Load: {config_load} -> {dump_data(config_data)}") + print(f"[{config_load}] Load: {config_load} -> {dump_data(config_data)}") + except ValueError as e: + print(f"Could not load settings: {e}") + + try: + config_load = 'TestB' + config_data = sl.load_settings( + config_load, + { + "email": [ + "split:,", + "mandatory:yes", + "check:string.email.basic" + ], + "email_bad": [ + "split:,", + "mandatory:yes", + "check:string.email.basic" + ] + } + ) + print(f"[{config_load}] Load: {config_load} -> {dump_data(config_data)}") except ValueError as e: print(f"Could not load settings: {e}") diff --git a/test-run/logging_handling/log.py b/test-run/logging_handling/log.py index 052111c..8b8cc30 100644 --- a/test-run/logging_handling/log.py +++ b/test-run/logging_handling/log.py @@ -18,7 +18,8 @@ def main(): log_path=script_path.joinpath('log', 'test.log'), log_name="Test Log", log_settings={ - "log_level_console": 'DEBUG', + # "log_level_console": 'DEBUG', + "log_level_console": None, "log_level_file": 'DEBUG', # "console_color_output_enabled": False, } diff --git a/uv.lock b/uv.lock index 44a5406..982f73d 100644 --- a/uv.lock +++ b/uv.lock @@ -44,7 +44,7 @@ wheels = [ [[package]] name = "corelibs" -version = "0.11.0" +version = "0.12.0" source = { editable = "." } dependencies = [ { name = "jmespath" },