"""AsusRouter Registry.""" from __future__ import annotations import threading from typing import Any from asusrouter.tools.types import ARCallableType class ARCallableRegistryBase: """Simple, thread-safe registry mapping source classes -> named callables. - Modules register their functions explicitly. - Lookup resolves by class then MRO (most-specific first). """ def __init__(self) -> None: """Initialize the registry.""" self._map: dict[type, dict[str, ARCallableType]] = {} self._lock = threading.RLock() def register(self, source_cls: type, **callables: ARCallableType) -> None: """Register one or more named callables for `source_cls`.""" with self._lock: entry = self._map.setdefault(source_cls, {}) entry.update(callables) def unregister(self, source_cls: type) -> None: """Remove all registrations for a source class.""" with self._lock: self._map.pop(source_cls, None) def clear(self) -> None: """Clear all registrations.""" with self._lock: self._map.clear() def get_callable(self, source: Any, name: str) -> ARCallableType | None: """Return the callable for `source` (instance or class) or None. Resolves by checking the exact class and then walking the MRO. """ cls = source if isinstance(source, type) else type(source) with self._lock: for base in getattr(cls, "__mro__", ()): entry = self._map.get(base) if entry and name in entry: return entry[name] return None def get_all_for(self, source: Any) -> dict[str, ARCallableType]: """Return all resolved callables for `source` by name (MRO merged). More specific classes override less specific ones. """ cls = source if isinstance(source, type) else type(source) merged: dict[str, ARCallableType] = {} with self._lock: # walk MRO from base -> subclass so subclasses override for base in reversed(getattr(cls, "__mro__", ())): entry = self._map.get(base) if entry: merged.update(entry) return merged ARCallableRegistry: ARCallableRegistryBase = ARCallableRegistryBase()