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