| 15 | |
| 16 | |
| 17 | class 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] = instance |
| 29 | 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 = None |
| 52 | 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) |
no outgoing calls
searching dependent graphs…