codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import logging4import typing5from collections.abc import Mapping6 7from ..utils import signals8from ._api import Contentview9from ._api import Metadata10 11logger = logging.getLogger(__name__)12 13 14def _on_change(view: Contentview) -> None: ...15 16 17class ContentviewRegistry(Mapping[str, Contentview]):18 def __init__(self):19 self._by_name: dict[str, Contentview] = {}20 self.on_change = signals.SyncSignal(_on_change)21 22 def register(self, instance: Contentview | type[Contentview]) -> None:23 if isinstance(instance, type):24 instance = instance()25 name = instance.name.lower()26 if name in self._by_name:27 logger.info(f"Replacing existing {name} contentview.")28 self._by_name[name] = instance29 self.on_change.send(instance)30 31 def available_views(self) -> list[str]:32 return ["auto", *sorted(self._by_name.keys())]33 34 def get_view(35 self, data: bytes, metadata: Metadata, view_name: str = "auto"36 ) -> Contentview:37 """38 Get the best contentview for the given data and metadata.39 40 If `view_name` is "auto" or the provided view not found,41 the best matching contentview based on `render_priority` will be returned.42 """43 if view_name != "auto":44 try:45 return self[view_name.lower()]46 except KeyError:47 logger.warning(48 f"Unknown contentview {view_name!r}, selecting best match instead."49 )50 51 max_prio: tuple[float, Contentview] | None = None52 for name, view in self._by_name.items():53 try:54 priority = view.render_priority(data, metadata)55 assert isinstance(priority, (int, float)), (56 f"render_priority for {view.name} did not return a number."57 )58 except Exception:59 logger.exception(f"Error in {view.name}.render_priority")60 else:61 if max_prio is None or max_prio[0] < priority:62 max_prio = (priority, view)63 assert max_prio, "At least one view needs to have a working `render_priority`."64 return max_prio[1]65 66 def __iter__(self) -> typing.Iterator[str]:67 return iter(self._by_name)68 69 def __getitem__(self, item: str) -> Contentview:70 return self._by_name[item.lower()]71 72 def __len__(self):73 return len(self._by_name)74 