diff --git a/libervia/backend/tools/config.py b/libervia/backend/tools/config.py index 797e73e8..67d1e7c7 100644 --- a/libervia/backend/tools/config.py +++ b/libervia/backend/tools/config.py @@ -22,8 +22,15 @@ from collections import defaultdict from pathlib import Path from socket import getfqdn -from typing import Annotated, Any, Literal, Self, TypeVar, cast +from tomllib import TOMLDecodeError +from typing import Annotated, Literal, Self, TypeVar, cast +from libervia.backend.core import exceptions +from libervia.backend.core.constants import Const as C, xdg_data_home +from libervia.backend.core.i18n import _ +from libervia.backend.core.log import getLogger +from libervia.backend.models.types import JIDType +from libervia.backend.tools.common.files_utils import get_downloads_dir from pydantic import ( AfterValidator, BaseModel, @@ -42,21 +49,14 @@ from pydantic_settings import ( TomlConfigSettingsSource, ) -from libervia.backend.core import exceptions -from libervia.backend.core.constants import Const as C, xdg_data_home -from libervia.backend.core.i18n import _ -from libervia.backend.core.log import getLogger -from libervia.backend.models.types import JIDType -from libervia.backend.tools.common.files_utils import get_downloads_dir - log = getLogger(__name__) expanduser_validator = AfterValidator(lambda p: p.expanduser().resolve()) cached_config: "Settings|None" = None -DynamicSettingsRegister = dict[str, "type[DynamicSettingsBase]"] -DynamicSettings: dict[str, DynamicSettingsRegister | type["DynamicSettingsBase"]] = ( - defaultdict(DynamicSettingsRegister) +dynamicSettingsRegister = dict[str, "type[DynamicSettingsBase]"] +dynamicSettings: dict[str, dynamicSettingsRegister | type["DynamicSettingsBase"]] = ( + defaultdict(dynamicSettingsRegister) ) @@ -123,7 +123,7 @@ class DynamicSettingsBase(ConfigOptions): # normalized on plugin import. sub_key = sub_key.replace("-", "_").lower() cls._sub_key = sub_key - register = DynamicSettings[key] + register = dynamicSettings[key] assert isinstance(register, dict) if sub_key in register: raise exceptions.ConflictError( @@ -133,7 +133,7 @@ class DynamicSettingsBase(ConfigOptions): cls._import_name = plugin_info.get(C.PI_IMPORT_NAME) else: cls._sub_key = None - DynamicSettings[key] = cls + dynamicSettings[key] = cls class PluginSettings(DynamicSettingsBase): @@ -189,6 +189,10 @@ class FrontendSettings(DynamicSettingsBase): super()._init_subclass(key=key) +class DynamicSettingsCollection(BaseModel): + """Base class for collection of DynamicSettings.""" + + D = TypeVar("D", bound=DynamicSettingsBase) @@ -414,9 +418,12 @@ class Settings(BaseSettings): default_factory=dict, description="Application specific settings, used by app manager.", ) - plugin: dict = Field(default_factory=dict, description="Plugin specific settings.") - component: dict = Field( - default_factory=dict, description="Component specific settings." + plugin: DynamicSettingsCollection = Field( + default_factory=DynamicSettingsCollection, description="Plugin specific settings." + ) + component: DynamicSettingsCollection = Field( + default_factory=DynamicSettingsCollection, + description="Component specific settings.", ) @field_validator("bridges", mode="after") @@ -519,10 +526,11 @@ def parse_main_conf(log_filenames: bool = False, ignore_cache=False) -> Settings if ignore_cache or cached_config is None: dynamic_settings_kw = {} - for key, settings in DynamicSettings.items(): + for key, settings in dynamicSettings.items(): if isinstance(settings, dict): NewSettingsModel = create_model( - f"{key.title()}Settings", + f"{key.title()}SettingsCollection", + __base__=DynamicSettingsCollection, **cast( dict, { @@ -549,10 +557,12 @@ def parse_main_conf(log_filenames: bool = False, ignore_cache=False) -> Settings try: cached_config = LiberviaSettings() except ValidationError as e: - log.error(f"Settings are invalid, please fix your configuration: {e}") - raise exceptions.ConfigError - except: + raise exceptions.ConfigError(e) + except TOMLDecodeError as e: + log.error(f"You configuration file is invalid: {e}") + raise exceptions.ConfigError from e + except Exception as e: log.exception("Can't parse settings.") - raise exceptions.InternalError + raise exceptions.InternalError from e return cached_config