from yuxi.channel.plugins.protocol import ChannelMeta from yuxi.channel.ports import MetaPort from yuxi.utils.logging_config import logger class ChannelRegistry: """渠道插件注册表,支持别名解析。""" def __init__(self) -> None: self._plugins: dict[str, MetaPort] = {} self._alias_to_primary: dict[str, str] = {} def register(self, plugin: MetaPort) -> None: meta = plugin.get_meta() existing = self._plugins.get(meta.channel_type) if existing is not None and existing is not plugin: logger.warning( "Channel type '%s' is already registered, overwriting with new plugin", meta.channel_type, ) self._plugins[meta.channel_type] = plugin for alias in meta.aliases: existing_primary = self._alias_to_primary.get(alias) if existing_primary is not None and existing_primary != meta.channel_type: logger.warning( "Channel alias '%s' already registered to '%s', skipping registration for '%s'", alias, existing_primary, meta.channel_type, ) continue self._plugins[alias] = plugin self._alias_to_primary[alias] = meta.channel_type def register_primary(self, plugin: MetaPort) -> None: meta = plugin.get_meta() existing = self._plugins.get(meta.channel_type) if existing is not None and existing is not plugin: logger.warning( "Channel type '%s' is already registered, overwriting with new plugin", meta.channel_type, ) self._plugins[meta.channel_type] = plugin def unregister(self, channel_type: str) -> None: plugin = self._plugins.pop(channel_type, None) if plugin is None: return meta = plugin.get_meta() primary = meta.channel_type for alias in meta.aliases: if self._alias_to_primary.get(alias) == primary: self._plugins.pop(alias, None) self._alias_to_primary.pop(alias, None) def get_plugin(self, channel_type: str) -> MetaPort | None: return self._plugins.get(channel_type) def get_plugins_by_port(self, port: type) -> list[MetaPort]: seen: set[int] = set() result: list[MetaPort] = [] for plugin in self._plugins.values(): pid = id(plugin) if pid in seen: continue seen.add(pid) if isinstance(plugin, port): result.append(plugin) return result def has_port(self, channel_type: str, port: type) -> bool: plugin = self._plugins.get(channel_type) return plugin is not None and isinstance(plugin, port) def list_plugins(self) -> list[ChannelMeta]: seen: set[int] = set() result: list[ChannelMeta] = [] for plugin in self._plugins.values(): pid = id(plugin) if pid in seen: continue seen.add(pid) result.append(plugin.get_meta()) return result _GLOBAL_REGISTRY: ChannelRegistry | None = None def get_registry() -> ChannelRegistry: global _GLOBAL_REGISTRY if _GLOBAL_REGISTRY is None: _GLOBAL_REGISTRY = ChannelRegistry() return _GLOBAL_REGISTRY