Source code for betty.plugin.config

"""
Provide plugin configuration.
"""

from collections.abc import Sequence
from typing import Self, TypeVar, Generic

from typing_extensions import override

from betty.assertion import (
    RequiredField,
    assert_str,
    assert_record,
    OptionalField,
    assert_setattr,
    assert_mapping,
)
from betty.config import Configuration
from betty.config.collections.mapping import ConfigurationMapping
from betty.locale.localizable import ShorthandStaticTranslations
from betty.locale.localizable.config import (
    StaticTranslationsLocalizableConfigurationAttr,
)
from betty.machine_name import assert_machine_name, MachineName
from betty.plugin import Plugin
from betty.serde.dump import Dump, minimize, VoidableDump

_PluginCoT = TypeVar("_PluginCoT", bound=Plugin, covariant=True)


[docs] class PluginConfiguration(Configuration): """ Configure a single plugin. """ label = StaticTranslationsLocalizableConfigurationAttr("label") description = StaticTranslationsLocalizableConfigurationAttr( "description", required=False )
[docs] def __init__( self, plugin_id: MachineName, label: ShorthandStaticTranslations, *, description: ShorthandStaticTranslations | None = None, ): super().__init__() self._id = assert_machine_name()(plugin_id) self.label = label if description is not None: self.description = description
@property def id(self) -> str: """ The configured plugin ID. """ return self._id
[docs] @override def update(self, other: Self) -> None: self._id = other.id self.label.update(other.label) self.description.update(other.description)
[docs] @override def load(self, dump: Dump) -> None: assert_record( RequiredField("id", assert_machine_name() | assert_setattr(self, "_id")), RequiredField("label", self.label.load), OptionalField("description", self.description.load), )(dump)
[docs] @override def dump(self) -> VoidableDump: return minimize( { "id": self.id, "label": self.label.dump(), "description": self.description.dump(), } )
_PluginConfigurationT = TypeVar("_PluginConfigurationT", bound=PluginConfiguration)
[docs] class PluginConfigurationMapping( ConfigurationMapping[str, _PluginConfigurationT], Generic[_PluginCoT, _PluginConfigurationT], ): """ Configure a collection of plugins. """ @property def plugins(self) -> Sequence[type[_PluginCoT]]: """ The plugins for this configuration. You SHOULD NOT cache the value anywhere, as it *will* change when this configuration changes. """ return tuple( self._create_plugin(plugin_configuration) for plugin_configuration in self.values() ) def _create_plugin(self, configuration: _PluginConfigurationT) -> type[_PluginCoT]: """ The plugin (class) for the given configuration. """ raise NotImplementedError @override def _minimize_item_dump(self) -> bool: return True @override def _get_key(self, configuration: _PluginConfigurationT) -> str: return configuration.id @override @classmethod def _load_key(cls, item_dump: Dump, key_dump: str) -> Dump: dump_mapping = assert_mapping()(item_dump) assert_str()(key_dump) dump_mapping["id"] = key_dump return dump_mapping @override def _dump_key(self, item_dump: VoidableDump) -> tuple[VoidableDump, str]: dump_mapping = assert_mapping()(item_dump) return dump_mapping, dump_mapping.pop("id")
[docs] class PluginConfigurationPluginConfigurationMapping( PluginConfigurationMapping[_PluginCoT, PluginConfiguration], Generic[_PluginCoT] ): """ Configure a collection of plugins using :py:class:`betty.plugin.config.PluginConfiguration`. """
[docs] @override def load_item(self, dump: Dump) -> PluginConfiguration: item = PluginConfiguration("-", "") item.load(dump) return item
@classmethod def _create_default_item(cls, configuration_key: str) -> PluginConfiguration: return PluginConfiguration(configuration_key, {})