RFC/1630 Signals (#2042)

* Temp working version of initial signal api

* fix signals router finalizing

* Additional tests

* Add event test

* finalize test

* remove old comment

* Add some missing annotations

* multiple apps per BP support

* deepsource?

* rtemove deepsource

* nominal change

* fix blueprints test

* trivial change to trigger build

* signal docstring

* squash

* squash

* Add a couple new tests

* Add some suggestions from review

* Remove inaccessible code

* Change where to condition
This commit is contained in:
Adam Hopkins
2021-03-14 10:09:07 +02:00
committed by GitHub
parent 1165663ec1
commit 824f41d6e0
16 changed files with 658 additions and 59 deletions

View File

@@ -43,8 +43,8 @@ class ListenerMixin:
async def before_server_start(app, loop):
...
`See user guide
<https://sanicframework.org/guide/basics/listeners.html#listeners>`_
`See user guide re: listeners
<https://sanicframework.org/guide/basics/listeners.html#listeners>`__
:param event: event to listen to
"""

View File

@@ -19,8 +19,8 @@ class MiddlewareMixin:
Can either be called as *@app.middleware* or
*@app.middleware('request')*
`See user guide
<https://sanicframework.org/guide/basics/middleware.html>`_
`See user guide re: middleware
<https://sanicframework.org/guide/basics/middleware.html>`__
:param: middleware_or_request: Optional parameter to use for
identifying which type of middleware is being registered.

View File

@@ -32,7 +32,7 @@ class RouteMixin:
self.name = ""
self.strict_slashes: Optional[bool] = False
def _apply_route(self, route: FutureRoute) -> Route:
def _apply_route(self, route: FutureRoute) -> List[Route]:
raise NotImplementedError # noqa
def _apply_static(self, static: FutureStatic) -> Route:

62
sanic/mixins/signals.py Normal file
View File

@@ -0,0 +1,62 @@
from typing import Any, Callable, Dict, Set
from sanic.models.futures import FutureSignal
from sanic.models.handler_types import SignalHandler
from sanic.signals import Signal
class HashableDict(dict):
def __hash__(self):
return hash(tuple(sorted(self.items())))
class SignalMixin:
def __init__(self, *args, **kwargs) -> None:
self._future_signals: Set[FutureSignal] = set()
def _apply_signal(self, signal: FutureSignal) -> Signal:
raise NotImplementedError # noqa
def signal(
self,
event: str,
*,
apply: bool = True,
condition: Dict[str, Any] = None,
) -> Callable[[SignalHandler], FutureSignal]:
"""
For creating a signal handler, used similar to a route handler:
.. code-block:: python
@app.signal("foo.bar.<thing>")
async def signal_handler(thing, **kwargs):
print(f"[signal_handler] {thing=}", kwargs)
:param event: Representation of the event in ``one.two.three`` form
:type event: str
:param apply: For lazy evaluation, defaults to True
:type apply: bool, optional
:param condition: For use with the ``condition`` argument in dispatch
filtering, defaults to None
:type condition: Dict[str, Any], optional
"""
def decorator(handler: SignalHandler):
nonlocal event
nonlocal apply
future_signal = FutureSignal(
handler, event, HashableDict(condition or {})
)
self._future_signals.add(future_signal)
if apply:
self._apply_signal(future_signal)
return future_signal
return decorator
def event(self, event: str):
raise NotImplementedError