from ast import Attribute, Await, Expr, NodeTransformer from typing import Any, List from sanic.log import logger from .base import BaseScheme class OptionalDispatchEvent(BaseScheme): ident = "ODE" SYNC_SIGNAL_NAMESPACES = "http." def __init__(self, app) -> None: super().__init__(app) self._sync_events() self._registered_events = [signal.name for signal in app.signal_router.routes] def visitors(self) -> List[NodeTransformer]: return [RemoveDispatch(self._registered_events)] def _sync_events(self): all_events = set() app_events = {} for app in self.app.__class__._app_registry.values(): if app.state.server_info: app_events[app] = {signal.name for signal in app.signal_router.routes} all_events.update(app_events[app]) for app, events in app_events.items(): missing = { x for x in all_events.difference(events) if any(x.startswith(y) for y in self.SYNC_SIGNAL_NAMESPACES) } if missing: was_finalized = app.signal_router.finalized if was_finalized: # no cov app.signal_router.reset() for event in missing: app.signal(event)(self.noop) if was_finalized: # no cov app.signal_router.finalize() @staticmethod async def noop(**_): # no cov ... class RemoveDispatch(NodeTransformer): def __init__(self, registered_events) -> None: self._registered_events = registered_events def visit_Expr(self, node: Expr) -> Any: call = node.value if isinstance(call, Await): call = call.value func = getattr(call, "func", None) args = getattr(call, "args", None) if not func or not args: return node if isinstance(func, Attribute) and func.attr == "dispatch": event = args[0] if hasattr(event, "s"): event_name = getattr(event, "value", event.s) if self._not_registered(event_name): logger.debug( f"Disabling event: {event_name}", extra={"verbosity": 2}, ) return None return node def _not_registered(self, event_name): dynamic = [] for event in self._registered_events: if event.endswith(">"): namespace_concern, _ = event.rsplit(".", 1) dynamic.append(namespace_concern) namespace_concern, _ = event_name.rsplit(".", 1) return ( event_name not in self._registered_events and namespace_concern not in dynamic )