Source code for betty.serde.format

"""
Provide serialization formats.
"""

from __future__ import annotations

from abc import abstractmethod
from typing import Sequence, TYPE_CHECKING, final

from typing_extensions import override

from betty.assertion.error import AssertionFailed
from betty.locale.localizable import Localizable, _
from betty.locale.localized import LocalizedStr
from betty.plugin import Plugin, PluginRepository
from betty.plugin.entry_point import EntryPointPluginRepository

if TYPE_CHECKING:
    from betty.serde.dump import Dump
    from betty.machine_name import MachineName
    from collections.abc import AsyncIterator
    from betty.typing import Voidable
    from betty.locale.localizer import Localizer


[docs] class FormatError(AssertionFailed): """ Raised when data that is being deserialized is provided in an unknown (undeserializable) format. """ pass
[docs] class Format(Plugin): """ Defines a (de)serialization format. """
[docs] @classmethod @abstractmethod def extensions(cls) -> set[str]: """ The file extensions this format can (de)serialize. """ pass
[docs] @abstractmethod def load(self, dump: str) -> Dump: """ Deserialize data. :raise FormatError: Raised when the dump could not be loaded. """ pass
[docs] @abstractmethod def dump(self, dump: Voidable[Dump]) -> str: """ Serialize data. """ pass
[docs] @final class FormatRepository(PluginRepository[Format]): """ Exposes the available (de)serialization formats. """
[docs] def __init__(self): super().__init__() self._upstream = EntryPointPluginRepository[Format]("betty.serde_format")
[docs] @override async def get(self, plugin_id: MachineName) -> type[Format]: return await self._upstream.get(plugin_id)
@override def __aiter__(self) -> AsyncIterator[type[Format]]: return self._upstream.__aiter__()
[docs] async def extensions(self) -> set[str]: """ All file extensions supported by the formats in this repository. """ return { extension async for serde_format in self for extension in serde_format.extensions() }
FORMAT_REPOSITORY = FormatRepository() """ The (de)serialization format plugin repository. Read more about :doc:`/development/plugin/serde-format`. """
[docs] @final class FormatStr(Localizable): """ Localize and format a sequence of (de)serialization formats. """
[docs] def __init__(self, serde_formats: Sequence[type[Format]]): self._serde_formats = serde_formats
[docs] @override def localize(self, localizer: Localizer) -> LocalizedStr: return LocalizedStr( ", ".join( [ f"{extension} ({serde_format.plugin_label().localize(localizer)})" for serde_format in self._serde_formats for extension in serde_format.extensions() ] ) )
[docs] def format_for( available_formats: Sequence[type[Format]], extension: str ) -> type[Format]: """ Get the (de)serialization format for the given file extension. """ for available_format in available_formats: if extension in available_format.extensions(): return available_format raise FormatError( _( 'Unknown file format "{extension}". Supported formats are: {available_formats}.' ).format(extension=extension, available_formats=FormatStr(available_formats)) )