Add support for listener and signal prioritization

This commit is contained in:
Adam Hopkins 2023-09-14 08:43:43 +03:00
parent 86a414fd58
commit 0dd1948fd2
No known key found for this signature in database
GPG Key ID: 9F85EE6C807303FB
5 changed files with 35 additions and 12 deletions

View File

@ -432,7 +432,7 @@ class Sanic(
# -------------------------------------------------------------------- #
def register_listener(
self, listener: ListenerType[SanicVar], event: str
self, listener: ListenerType[SanicVar], event: str, *, priority: int = 0
) -> ListenerType[SanicVar]:
"""Register the listener for a given event.
@ -453,10 +453,14 @@ class Sanic(
raise BadRequest(f"Invalid event: {event}. Use one of: {valid}")
if "." in _event:
self.signal(_event.value)(
self.signal(_event.value, priority=priority)(
partial(self._listener, listener=listener)
)
else:
if priority:
error_logger.warning(
f"Priority is not supported for {_event.value}"
)
self.listeners[_event.value].append(listener)
return listener
@ -580,7 +584,7 @@ class Sanic(
return handler.handler
def _apply_listener(self, listener: FutureListener):
return self.register_listener(listener.listener, listener.event)
return self.register_listener(listener.listener, listener.event, priority=listener.priority)
def _apply_route(
self, route: FutureRoute, overwrite: bool = False
@ -632,7 +636,13 @@ class Sanic(
def _apply_signal(self, signal: FutureSignal) -> Signal:
with self.amend():
return self.signal_router.add(*signal)
return self.signal_router.add(
handler=signal.handler,
event=signal.event,
condition=signal.condition,
exclusive=signal.exclusive,
priority=signal.priority,
)
@overload
def dispatch(
@ -1399,7 +1409,7 @@ class Sanic(
if not hasattr(handler, "is_websocket"):
raise ServerError(
f"Invalid response type {response!r} "
"(need HTTPResponse)"
"(need HTTPResponse)"
)
except CancelledError: # type: ignore

View File

@ -1,7 +1,7 @@
from enum import Enum, auto
from functools import partial
from typing import Callable, List, Optional, Union, overload
from operator import attrgetter
from sanic.base.meta import SanicMeta
from sanic.exceptions import BadRequest
from sanic.models.futures import FutureListener
@ -38,6 +38,8 @@ class ListenerMixin(metaclass=SanicMeta):
listener_or_event: ListenerType[Sanic],
event_or_none: str,
apply: bool = ...,
*,
priority: int = 0,
) -> ListenerType[Sanic]:
...
@ -47,6 +49,8 @@ class ListenerMixin(metaclass=SanicMeta):
listener_or_event: str,
event_or_none: None = ...,
apply: bool = ...,
*,
priority: int = 0,
) -> Callable[[ListenerType[Sanic]], ListenerType[Sanic]]:
...
@ -55,6 +59,8 @@ class ListenerMixin(metaclass=SanicMeta):
listener_or_event: Union[ListenerType[Sanic], str],
event_or_none: Optional[str] = None,
apply: bool = True,
*,
priority: int = 0,
) -> Union[
ListenerType[Sanic],
Callable[[ListenerType[Sanic]], ListenerType[Sanic]],
@ -81,6 +87,7 @@ class ListenerMixin(metaclass=SanicMeta):
listener_or_event (Union[ListenerType[Sanic], str]): A listener function or an event name.
event_or_none (Optional[str]): The event name to listen for if `listener_or_event` is a function. Defaults to `None`.
apply (bool): Whether to apply the listener immediately. Defaults to `True`.
priority (int): The priority of the listener. Defaults to `0`.
Returns:
Union[ListenerType[Sanic], Callable[[ListenerType[Sanic]], ListenerType[Sanic]]]: The listener or a callable that takes a listener.
@ -96,7 +103,7 @@ class ListenerMixin(metaclass=SanicMeta):
""" # noqa: E501
def register_listener(
listener: ListenerType[Sanic], event: str
listener: ListenerType[Sanic], event: str, priority: int = 0
) -> ListenerType[Sanic]:
"""A helper function to register a listener for an event.
@ -112,7 +119,7 @@ class ListenerMixin(metaclass=SanicMeta):
"""
nonlocal apply
future_listener = FutureListener(listener, event)
future_listener = FutureListener(listener, event, priority)
self._future_listeners.append(future_listener)
if apply:
self._apply_listener(future_listener)
@ -123,9 +130,9 @@ class ListenerMixin(metaclass=SanicMeta):
raise BadRequest(
"Invalid event registration: Missing event name."
)
return register_listener(listener_or_event, event_or_none)
return register_listener(listener_or_event, event_or_none, priority)
else:
return partial(register_listener, event=listener_or_event)
return partial(register_listener, event=listener_or_event, priority=priority)
def main_process_start(
self, listener: ListenerType[Sanic]

View File

@ -24,6 +24,7 @@ class SignalMixin(metaclass=SanicMeta):
apply: bool = True,
condition: Optional[Dict[str, Any]] = None,
exclusive: bool = True,
priority: int = 0,
) -> Callable[[SignalHandler], SignalHandler]:
"""
For creating a signal handler, used similar to a route handler:
@ -51,7 +52,7 @@ class SignalMixin(metaclass=SanicMeta):
def decorator(handler: SignalHandler):
future_signal = FutureSignal(
handler, event_value, HashableDict(condition or {}), exclusive
handler, event_value, HashableDict(condition or {}), exclusive, priority
)
self._future_signals.add(future_signal)

View File

@ -33,6 +33,7 @@ class FutureRoute(NamedTuple):
class FutureListener(NamedTuple):
listener: ListenerType
event: str
priority: int
class FutureMiddleware(NamedTuple):
@ -65,6 +66,7 @@ class FutureSignal(NamedTuple):
event: str
condition: Optional[Dict[str, str]]
exclusive: bool
priority: int
class FutureRegistry(set):

View File

@ -250,6 +250,8 @@ class SignalRouter(BaseRouter):
event: str,
condition: Optional[Dict[str, Any]] = None,
exclusive: bool = True,
*,
priority: int = 0,
) -> Signal:
event_definition = event
parts = self._build_event_parts(event)
@ -268,6 +270,7 @@ class SignalRouter(BaseRouter):
handler,
name=name,
append=True,
priority=priority,
) # type: ignore
signal.ctx.exclusive = exclusive