tools (config): QoL improvements:

- correct the case of `dynamicSettingsRegister` and `dynamicSettings` which are `dict`, not
  classes.
- Use new `DynamicSettingsCollection` as base class for classes dynamically built during
  parsing, to avoid name conflict.
- Use `DynamicSettingsCollection` instead of `dict` in dynamic field (`plugin` and
  `component`). This correct inconsistency, and make linters happy.
- Catch `TOMLDecodeError` when parsing config, to show a more readable error message
  instead of full backtrace.
This commit is contained in:
Goffi 2026-03-30 12:05:47 +02:00
commit 60753f4d71

View file

@ -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