"""
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]
@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