Source code for betty.plugin.proxy

"""
Provide tools for proxying plugin management to other tools.
"""

from collections.abc import AsyncIterator
from contextlib import suppress
from typing import Generic, TypeVar, final

from typing_extensions import override, overload

from betty.factory import Factory, FactoryError
from betty.machine_name import MachineName
from betty.plugin import PluginRepository, Plugin, PluginNotFound

_T = TypeVar("_T")
_PluginT = TypeVar("_PluginT", bound=Plugin)


[docs] @final class ProxyPluginRepository(PluginRepository[_PluginT], Generic[_PluginT]): """ Expose multiple other plugin repositories as one unified repository. """
[docs] def __init__( self, *upstreams: PluginRepository[_PluginT], factory: Factory | None = None, ): super().__init__(factory=factory) self._upstreams = upstreams
@overload async def new_target(self, cls: type[_T]) -> _T: pass @overload async def new_target(self, cls: MachineName) -> _PluginT: pass
[docs] @override async def new_target(self, cls: type[_T] | MachineName) -> _T | _PluginT: with suppress(FactoryError): return await super().new_target(cls) resolved_cls = await self.get(cls) if isinstance(cls, str) else cls for upstream in self._upstreams: if issubclass(resolved_cls, Plugin): try: await upstream.get(resolved_cls.plugin_id()) except PluginNotFound: continue with suppress(PluginNotFound, FactoryError): return await upstream.new_target(resolved_cls) raise FactoryError()
[docs] @override async def get(self, plugin_id: MachineName) -> type[_PluginT]: for upstream in self._upstreams: try: return await upstream.get(plugin_id) except PluginNotFound: pass raise PluginNotFound.new(plugin_id, await self.select()) from None
@override async def __aiter__(self) -> AsyncIterator[type[_PluginT]]: seen = set() for upstream in self._upstreams: async for plugin in upstream: if plugin.plugin_id() not in seen: seen.add(plugin.plugin_id()) yield plugin