74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import typing
|
|
from collections.abc import Mapping
|
|
|
|
from ..utils import signals
|
|
from ._api import Contentview
|
|
from ._api import Metadata
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _on_change(view: Contentview) -> None: ...
|
|
|
|
|
|
class ContentviewRegistry(Mapping[str, Contentview]):
|
|
def __init__(self):
|
|
self._by_name: dict[str, Contentview] = {}
|
|
self.on_change = signals.SyncSignal(_on_change)
|
|
|
|
def register(self, instance: Contentview | type[Contentview]) -> None:
|
|
if isinstance(instance, type):
|
|
instance = instance()
|
|
name = instance.name.lower()
|
|
if name in self._by_name:
|
|
logger.info(f"Replacing existing {name} contentview.")
|
|
self._by_name[name] = instance
|
|
self.on_change.send(instance)
|
|
|
|
def available_views(self) -> list[str]:
|
|
return ["auto", *sorted(self._by_name.keys())]
|
|
|
|
def get_view(
|
|
self, data: bytes, metadata: Metadata, view_name: str = "auto"
|
|
) -> Contentview:
|
|
"""
|
|
Get the best contentview for the given data and metadata.
|
|
|
|
If `view_name` is "auto" or the provided view not found,
|
|
the best matching contentview based on `render_priority` will be returned.
|
|
"""
|
|
if view_name != "auto":
|
|
try:
|
|
return self[view_name.lower()]
|
|
except KeyError:
|
|
logger.warning(
|
|
f"Unknown contentview {view_name!r}, selecting best match instead."
|
|
)
|
|
|
|
max_prio: tuple[float, Contentview] | None = None
|
|
for name, view in self._by_name.items():
|
|
try:
|
|
priority = view.render_priority(data, metadata)
|
|
assert isinstance(priority, (int, float)), (
|
|
f"render_priority for {view.name} did not return a number."
|
|
)
|
|
except Exception:
|
|
logger.exception(f"Error in {view.name}.render_priority")
|
|
else:
|
|
if max_prio is None or max_prio[0] < priority:
|
|
max_prio = (priority, view)
|
|
assert max_prio, "At least one view needs to have a working `render_priority`."
|
|
return max_prio[1]
|
|
|
|
def __iter__(self) -> typing.Iterator[str]:
|
|
return iter(self._by_name)
|
|
|
|
def __getitem__(self, item: str) -> Contentview:
|
|
return self._by_name[item.lower()]
|
|
|
|
def __len__(self):
|
|
return len(self._by_name)
|