Source code for composeui.core.registry

"""Map abstract renderer keys to each backend's concrete renderer classes.

Adding a backend means adding one `RendererType` member here, with its extra, one
``RENDERERS`` mapping in the backend's renderers module, and
``APPLICATION``/``MODEL_APPLICATION`` in its application module.
"""

from __future__ import annotations

import enum
import importlib
from typing import TYPE_CHECKING, Any, NamedTuple

if TYPE_CHECKING:
    from composeui.core.renderer import Renderer

__all__ = ["Backend", "RendererType", "create_renderer"]


[docs] class Backend(NamedTuple): """A backend's modules, by dotted path: none is imported until the backend is used.""" #: exposes the ``RENDERERS`` mapping renderers: str #: exposes ``APPLICATION`` and ``MODEL_APPLICATION``, the backend's application classes applications: str #: the package's extra that installs what the backend imports, e.g. ``"web"`` extra: str #: the top-level modules the extra installs: `run` names those missing packages: tuple[str, ...]
[docs] class RendererType(enum.Enum): """A renderer backend, identified by its modules.""" QT = Backend( "composeui.qtcore.qtrenderer", "composeui.qtcore.qtapplication", "pyside6", ("PySide6", "pyqtgraph", "qtpy"), ) TEXTUAL = Backend( "composeui.textualcore.textualrenderer", "composeui.textualcore.textualapplication", "tui", ("textual", "textual_fspicker", "textual_plotext"), ) WEB = Backend( "composeui.webcore.webrenderer", "composeui.webcore.webapplication", "web", ("itsdangerous", "markdown_it", "markupsafe", "python_multipart", "starlette", "uvicorn"), )
def has_renderer(renderer_type: RendererType, key: str) -> bool: """Tell whether `renderer_type`'s backend registers `key`, so it shows such views.""" return key in importlib.import_module(renderer_type.value.renderers).RENDERERS def renders_on_demand(renderer_type: RendererType, key: str) -> bool: """Whether `renderer_type`'s renderer of `key` builds the pages or tabs not shown once shown.""" factory = importlib.import_module(renderer_type.value.renderers).RENDERERS.get(key) return bool(getattr(factory, "renders_on_demand", False))
[docs] def create_renderer(renderer_type: RendererType, key: str, view: Any) -> Renderer[Any, Any] | None: """Create the renderer registered under `key` by `renderer_type`'s backend. Returns None when the backend doesn't register `key`, meaning it doesn't support that widget: the view is then not rendered. """ module = importlib.import_module(renderer_type.value.renderers) factory = module.RENDERERS.get(key) if factory is None: return None return factory(view) # type: ignore[no-any-return]