From a8d4a7a3626d88fe0447ea46c81720bd66353ad0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 4 Apr 2023 22:52:13 +0200 Subject: [PATCH] Added JSONProcessor to utils & Added JSONProcessor as settings parser --- src/cpl_core/configuration/configuration.py | 14 ++- .../configuration/configuration_model_abc.py | 7 +- src/cpl_core/database/database_settings.py | 99 ++++++++++--------- src/cpl_core/logging/logging_settings.py | 38 ++++--- src/cpl_core/mailing/email_client_settings.py | 38 +++---- src/cpl_core/time/time_format_settings.py | 38 ++++--- src/cpl_core/utils/json_processor.py | 41 ++++++++ .../custom/general/src/general/application.py | 2 +- 8 files changed, 178 insertions(+), 99 deletions(-) create mode 100644 src/cpl_core/utils/json_processor.py diff --git a/src/cpl_core/configuration/configuration.py b/src/cpl_core/configuration/configuration.py index 4f8942d8..7e179274 100644 --- a/src/cpl_core/configuration/configuration.py +++ b/src/cpl_core/configuration/configuration.py @@ -25,6 +25,7 @@ from cpl_core.environment.application_environment import ApplicationEnvironment from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC from cpl_core.environment.environment_name_enum import EnvironmentNameEnum from cpl_core.type import T +from cpl_core.utils.json_processor import JSONProcessor class Configuration(ConfigurationABC): @@ -279,7 +280,18 @@ class Configuration(ConfigurationABC): for key, value in config_from_file.items(): if sub.__name__ == key or sub.__name__.replace("Settings", "") == key: configuration = sub() - configuration.from_dict(value) + from_dict = getattr(configuration, "from_dict", None) + + if from_dict is not None and not hasattr(from_dict, "is_base_func"): + Console.set_foreground_color(ForegroundColorEnum.yellow) + Console.write_line( + f"{sub.__name__}.from_dict is deprecated. Instead, set attributes as typed arguments in __init__. They can be None by default!" + ) + Console.color_reset() + configuration.from_dict(value) + else: + configuration = JSONProcessor.process(sub, value) + self.add_configuration(sub, configuration) def add_configuration(self, key_type: T, value: any): diff --git a/src/cpl_core/configuration/configuration_model_abc.py b/src/cpl_core/configuration/configuration_model_abc.py index 674610b4..ff99238c 100644 --- a/src/cpl_core/configuration/configuration_model_abc.py +++ b/src/cpl_core/configuration/configuration_model_abc.py @@ -1,13 +1,18 @@ from abc import ABC, abstractmethod +def base_func(method): + method.is_base_func = True + return method + + class ConfigurationModelABC(ABC): @abstractmethod def __init__(self): r"""ABC for settings representation""" pass - @abstractmethod + @base_func def from_dict(self, settings: dict): r"""Converts attributes to dict diff --git a/src/cpl_core/database/database_settings.py b/src/cpl_core/database/database_settings.py index 987742da..7ca4ba19 100644 --- a/src/cpl_core/database/database_settings.py +++ b/src/cpl_core/database/database_settings.py @@ -1,27 +1,34 @@ -import traceback from typing import Optional from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console.console import Console -from cpl_core.console.foreground_color_enum import ForegroundColorEnum -from cpl_core.database.database_settings_name_enum import DatabaseSettingsNameEnum class DatabaseSettings(ConfigurationModelABC): r"""Represents settings for the database connection""" - def __init__(self): + def __init__( + self, + host: str = None, + port: int = None, + user: str = None, + password: str = None, + databse: str = None, + charset: str = None, + use_unicode: bool = None, + buffered: bool = None, + auth_plugin: bool = None, + ): ConfigurationModelABC.__init__(self) - self._host: Optional[str] = None - self._port: Optional[int] = None - self._user: Optional[str] = None - self._password: Optional[str] = None - self._databse: Optional[str] = None - self._charset: Optional[str] = None - self._use_unicode: Optional[bool] = None - self._buffered: Optional[bool] = None - self._auth_plugin: Optional[str] = None + self._host: Optional[str] = host + self._port: Optional[int] = port + self._user: Optional[str] = user + self._password: Optional[str] = password + self._databse: Optional[str] = databse + self._charset: Optional[str] = charset + self._use_unicode: Optional[bool] = use_unicode + self._buffered: Optional[bool] = buffered + self._auth_plugin: Optional[str] = auth_plugin @property def host(self) -> Optional[str]: @@ -59,35 +66,35 @@ class DatabaseSettings(ConfigurationModelABC): def auth_plugin(self) -> Optional[str]: return self._auth_plugin - def from_dict(self, settings: dict): - r"""Sets attributes from given dict - - Parameter: - settings: :class:`dict` - """ - try: - self._host = settings[DatabaseSettingsNameEnum.host.value] - if DatabaseSettingsNameEnum.port.value in settings: - self._port = settings[DatabaseSettingsNameEnum.port.value] - else: - self._port = 3306 - self._user = settings[DatabaseSettingsNameEnum.user.value] - self._password = settings[DatabaseSettingsNameEnum.password.value] - self._databse = settings[DatabaseSettingsNameEnum.database.value] - - if DatabaseSettingsNameEnum.charset.value in settings: - self._charset = settings[DatabaseSettingsNameEnum.charset.value] - - if DatabaseSettingsNameEnum.buffered.value in settings: - self._use_unicode = bool(settings[DatabaseSettingsNameEnum.use_unicode.value]) - - if DatabaseSettingsNameEnum.buffered.value in settings: - self._buffered = bool(settings[DatabaseSettingsNameEnum.buffered.value]) - - if DatabaseSettingsNameEnum.auth_plugin.value in settings: - self._auth_plugin = settings[DatabaseSettingsNameEnum.auth_plugin.value] - except Exception as e: - Console.set_foreground_color(ForegroundColorEnum.red) - Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") - Console.set_foreground_color(ForegroundColorEnum.default) + # def from_dict(self, settings: dict): + # r"""Sets attributes from given dict + # + # Parameter: + # settings: :class:`dict` + # """ + # try: + # self._host = settings[DatabaseSettingsNameEnum.host.value] + # if DatabaseSettingsNameEnum.port.value in settings: + # self._port = settings[DatabaseSettingsNameEnum.port.value] + # else: + # self._port = 3306 + # self._user = settings[DatabaseSettingsNameEnum.user.value] + # self._password = settings[DatabaseSettingsNameEnum.password.value] + # self._databse = settings[DatabaseSettingsNameEnum.database.value] + # + # if DatabaseSettingsNameEnum.charset.value in settings: + # self._charset = settings[DatabaseSettingsNameEnum.charset.value] + # + # if DatabaseSettingsNameEnum.buffered.value in settings: + # self._use_unicode = bool(settings[DatabaseSettingsNameEnum.use_unicode.value]) + # + # if DatabaseSettingsNameEnum.buffered.value in settings: + # self._buffered = bool(settings[DatabaseSettingsNameEnum.buffered.value]) + # + # if DatabaseSettingsNameEnum.auth_plugin.value in settings: + # self._auth_plugin = settings[DatabaseSettingsNameEnum.auth_plugin.value] + # except Exception as e: + # Console.set_foreground_color(ForegroundColorEnum.red) + # Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") + # Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") + # Console.set_foreground_color(ForegroundColorEnum.default) diff --git a/src/cpl_core/logging/logging_settings.py b/src/cpl_core/logging/logging_settings.py index 71d44d80..41f31277 100644 --- a/src/cpl_core/logging/logging_settings.py +++ b/src/cpl_core/logging/logging_settings.py @@ -11,12 +11,18 @@ from cpl_core.logging.logging_settings_name_enum import LoggingSettingsNameEnum class LoggingSettings(ConfigurationModelABC): r"""Representation of logging settings""" - def __init__(self): + def __init__( + self, + path: str = None, + filename: str = None, + console_log_level: LoggingLevelEnum = None, + file_log_level: LoggingLevelEnum = None, + ): ConfigurationModelABC.__init__(self) - self._path: Optional[str] = None - self._filename: Optional[str] = None - self._console: Optional[LoggingLevelEnum] = None - self._level: Optional[LoggingLevelEnum] = None + self._path: Optional[str] = path + self._filename: Optional[str] = filename + self._console: Optional[LoggingLevelEnum] = console_log_level + self._level: Optional[LoggingLevelEnum] = file_log_level @property def path(self) -> str: @@ -50,14 +56,14 @@ class LoggingSettings(ConfigurationModelABC): def level(self, level: LoggingLevelEnum) -> None: self._level = level - def from_dict(self, settings: dict): - try: - self._path = settings[LoggingSettingsNameEnum.path.value] - self._filename = settings[LoggingSettingsNameEnum.filename.value] - self._console = LoggingLevelEnum[settings[LoggingSettingsNameEnum.console_level.value]] - self._level = LoggingLevelEnum[settings[LoggingSettingsNameEnum.file_level.value]] - except Exception as e: - Console.set_foreground_color(ForegroundColorEnum.red) - Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") - Console.set_foreground_color(ForegroundColorEnum.default) + # def from_dict(self, settings: dict): + # try: + # self._path = settings[LoggingSettingsNameEnum.path.value] + # self._filename = settings[LoggingSettingsNameEnum.filename.value] + # self._console = LoggingLevelEnum[settings[LoggingSettingsNameEnum.console_level.value]] + # self._level = LoggingLevelEnum[settings[LoggingSettingsNameEnum.file_level.value]] + # except Exception as e: + # Console.set_foreground_color(ForegroundColorEnum.red) + # Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") + # Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") + # Console.set_foreground_color(ForegroundColorEnum.default) diff --git a/src/cpl_core/mailing/email_client_settings.py b/src/cpl_core/mailing/email_client_settings.py index 50dfdff4..4bcaca8a 100644 --- a/src/cpl_core/mailing/email_client_settings.py +++ b/src/cpl_core/mailing/email_client_settings.py @@ -1,20 +1,22 @@ -import traceback - from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC -from cpl_core.console.console import Console -from cpl_core.mailing.email_client_settings_name_enum import EMailClientSettingsNameEnum class EMailClientSettings(ConfigurationModelABC): r"""Representation of mailing settings""" - def __init__(self): + def __init__( + self, + host: str = None, + port: int = None, + user_name: str = None, + credentials: str = None, + ): ConfigurationModelABC.__init__(self) - self._host: str = "" - self._port: int = 0 - self._user_name: str = "" - self._credentials: str = "" + self._host: str = host + self._port: int = port + self._user_name: str = user_name + self._credentials: str = credentials @property def host(self) -> str: @@ -48,12 +50,12 @@ class EMailClientSettings(ConfigurationModelABC): def credentials(self, credentials: str) -> None: self._credentials = credentials - def from_dict(self, settings: dict): - try: - self._host = settings[EMailClientSettingsNameEnum.host.value] - self._port = settings[EMailClientSettingsNameEnum.port.value] - self._user_name = settings[EMailClientSettingsNameEnum.user_name.value] - self._credentials = settings[EMailClientSettingsNameEnum.credentials.value] - except Exception as e: - Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") + # def from_dict(self, settings: dict): + # try: + # self._host = settings[EMailClientSettingsNameEnum.host.value] + # self._port = settings[EMailClientSettingsNameEnum.port.value] + # self._user_name = settings[EMailClientSettingsNameEnum.user_name.value] + # self._credentials = settings[EMailClientSettingsNameEnum.credentials.value] + # except Exception as e: + # Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") + # Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") diff --git a/src/cpl_core/time/time_format_settings.py b/src/cpl_core/time/time_format_settings.py index d44e9d85..bcc7ec4f 100644 --- a/src/cpl_core/time/time_format_settings.py +++ b/src/cpl_core/time/time_format_settings.py @@ -10,12 +10,18 @@ from cpl_core.time.time_format_settings_names_enum import TimeFormatSettingsName class TimeFormatSettings(ConfigurationModelABC): r"""Representation of time format settings""" - def __init__(self): + def __init__( + self, + date_format: str = None, + time_format: str = None, + date_time_format: str = None, + date_time_log_format: str = None, + ): ConfigurationModelABC.__init__(self) - self._date_format: Optional[str] = None - self._time_format: Optional[str] = None - self._date_time_format: Optional[str] = None - self._date_time_log_format: Optional[str] = None + self._date_format: Optional[str] = date_format + self._time_format: Optional[str] = time_format + self._date_time_format: Optional[str] = date_time_format + self._date_time_log_format: Optional[str] = date_time_log_format @property def date_format(self) -> str: @@ -49,14 +55,14 @@ class TimeFormatSettings(ConfigurationModelABC): def date_time_log_format(self, date_time_now_format: str) -> None: self._date_time_log_format = date_time_now_format - def from_dict(self, settings: dict): - try: - self._date_format = settings[TimeFormatSettingsNamesEnum.date_format.value] - self._time_format = settings[TimeFormatSettingsNamesEnum.time_format.value] - self._date_time_format = settings[TimeFormatSettingsNamesEnum.date_time_format.value] - self._date_time_log_format = settings[TimeFormatSettingsNamesEnum.date_time_log_format.value] - except Exception as e: - Console.set_foreground_color(ForegroundColorEnum.red) - Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") - Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") - Console.set_foreground_color(ForegroundColorEnum.default) + # def from_dict(self, settings: dict): + # try: + # self._date_format = settings[TimeFormatSettingsNamesEnum.date_format.value] + # self._time_format = settings[TimeFormatSettingsNamesEnum.time_format.value] + # self._date_time_format = settings[TimeFormatSettingsNamesEnum.date_time_format.value] + # self._date_time_log_format = settings[TimeFormatSettingsNamesEnum.date_time_log_format.value] + # except Exception as e: + # Console.set_foreground_color(ForegroundColorEnum.red) + # Console.write_line(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings") + # Console.write_line(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}") + # Console.set_foreground_color(ForegroundColorEnum.default) diff --git a/src/cpl_core/utils/json_processor.py b/src/cpl_core/utils/json_processor.py new file mode 100644 index 00000000..e28176f8 --- /dev/null +++ b/src/cpl_core/utils/json_processor.py @@ -0,0 +1,41 @@ +import enum +from inspect import signature, Parameter + +from cpl_core.utils import String + + +class JSONProcessor: + @staticmethod + def process(_t: type, values: dict) -> object: + args = [] + + sig = signature(_t.__init__) + for param in sig.parameters.items(): + parameter = param[1] + if parameter.name == "self" or parameter.annotation == Parameter.empty: + continue + + name = String.first_to_upper(String.convert_to_camel_case(parameter.name)) + name_first_lower = String.first_to_lower(name) + if name in values or name_first_lower in values: + value = "" + if name in values: + value = values[name] + else: + value = values[name_first_lower] + + if isinstance(value, dict): + value = JSONProcessor.process(parameter.annotation, value) + + if issubclass(parameter.annotation, enum.Enum): + value = parameter.annotation[value] + + args.append(value) + + elif parameter.default != Parameter.empty: + args.append(parameter.default) + + else: + args.append(None) + + return _t(*args) diff --git a/tests/custom/general/src/general/application.py b/tests/custom/general/src/general/application.py index 16ff983d..f7f3ff72 100644 --- a/tests/custom/general/src/general/application.py +++ b/tests/custom/general/src/general/application.py @@ -57,4 +57,4 @@ class Application(ApplicationABC): Console.write_line("scope", scope) with self._services.create_scope() as s: Console.write_line("with scope", s) - self.test_send_mail() + # self.test_send_mail()