405 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from enum import Enum, auto
 | |
| from functools import partial
 | |
| from typing import Callable, List, Optional, Union, overload
 | |
| 
 | |
| from sanic.base.meta import SanicMeta
 | |
| from sanic.exceptions import BadRequest
 | |
| from sanic.models.futures import FutureListener
 | |
| from sanic.models.handler_types import ListenerType, Sanic
 | |
| 
 | |
| 
 | |
| class ListenerEvent(str, Enum):
 | |
|     def _generate_next_value_(name: str, *args) -> str:  # type: ignore
 | |
|         return name.lower()
 | |
| 
 | |
|     BEFORE_SERVER_START = "server.init.before"
 | |
|     AFTER_SERVER_START = "server.init.after"
 | |
|     BEFORE_SERVER_STOP = "server.shutdown.before"
 | |
|     AFTER_SERVER_STOP = "server.shutdown.after"
 | |
|     MAIN_PROCESS_START = auto()
 | |
|     MAIN_PROCESS_READY = auto()
 | |
|     MAIN_PROCESS_STOP = auto()
 | |
|     RELOAD_PROCESS_START = auto()
 | |
|     RELOAD_PROCESS_STOP = auto()
 | |
|     BEFORE_RELOAD_TRIGGER = auto()
 | |
|     AFTER_RELOAD_TRIGGER = auto()
 | |
| 
 | |
| 
 | |
| class ListenerMixin(metaclass=SanicMeta):
 | |
|     def __init__(self, *args, **kwargs) -> None:
 | |
|         self._future_listeners: List[FutureListener] = []
 | |
| 
 | |
|     def _apply_listener(self, listener: FutureListener):
 | |
|         raise NotImplementedError  # noqa
 | |
| 
 | |
|     @overload
 | |
|     def listener(
 | |
|         self,
 | |
|         listener_or_event: ListenerType[Sanic],
 | |
|         event_or_none: str,
 | |
|         apply: bool = ...,
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         ...
 | |
| 
 | |
|     @overload
 | |
|     def listener(
 | |
|         self,
 | |
|         listener_or_event: str,
 | |
|         event_or_none: None = ...,
 | |
|         apply: bool = ...,
 | |
|     ) -> Callable[[ListenerType[Sanic]], ListenerType[Sanic]]:
 | |
|         ...
 | |
| 
 | |
|     def listener(
 | |
|         self,
 | |
|         listener_or_event: Union[ListenerType[Sanic], str],
 | |
|         event_or_none: Optional[str] = None,
 | |
|         apply: bool = True,
 | |
|     ) -> Union[
 | |
|         ListenerType[Sanic],
 | |
|         Callable[[ListenerType[Sanic]], ListenerType[Sanic]],
 | |
|     ]:
 | |
|         """Create a listener for a specific event in the application's lifecycle.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         .. note::
 | |
|             Overloaded signatures allow for different ways of calling this method, depending on the types of the arguments.
 | |
| 
 | |
|             Usually, it is prederred to use one of the convenience methods such as `before_server_start` or `after_server_stop` instead of calling this method directly.
 | |
| 
 | |
|             ```python
 | |
|             @app.before_server_start
 | |
|             async def prefered_method(_):
 | |
|                 ...
 | |
| 
 | |
|             @app.listener("before_server_start")
 | |
|             async def not_prefered_method(_):
 | |
|                 ...
 | |
| 
 | |
|         Args:
 | |
|             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`.
 | |
| 
 | |
|         Returns:
 | |
|             Union[ListenerType[Sanic], Callable[[ListenerType[Sanic]], ListenerType[Sanic]]]: The listener or a callable that takes a listener.
 | |
| 
 | |
|         Example:
 | |
|             The following code snippet shows how you can use this method as a decorator:
 | |
| 
 | |
|             ```python
 | |
|             @bp.listener("before_server_start")
 | |
|             async def before_server_start(app, loop):
 | |
|                 ...
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
| 
 | |
|         def register_listener(
 | |
|             listener: ListenerType[Sanic], event: str
 | |
|         ) -> ListenerType[Sanic]:
 | |
|             """A helper function to register a listener for an event.
 | |
| 
 | |
|             Typically will not be called directly.
 | |
| 
 | |
|             Args:
 | |
|                 listener (ListenerType[Sanic]): The listener function to
 | |
|                     register.
 | |
|                 event (str): The event name to listen for.
 | |
| 
 | |
|             Returns:
 | |
|                 ListenerType[Sanic]: The listener function that was registered.
 | |
|             """
 | |
|             nonlocal apply
 | |
| 
 | |
|             future_listener = FutureListener(listener, event)
 | |
|             self._future_listeners.append(future_listener)
 | |
|             if apply:
 | |
|                 self._apply_listener(future_listener)
 | |
|             return listener
 | |
| 
 | |
|         if callable(listener_or_event):
 | |
|             if event_or_none is None:
 | |
|                 raise BadRequest(
 | |
|                     "Invalid event registration: Missing event name."
 | |
|                 )
 | |
|             return register_listener(listener_or_event, event_or_none)
 | |
|         else:
 | |
|             return partial(register_listener, event=listener_or_event)
 | |
| 
 | |
|     def main_process_start(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the main_process_start event.
 | |
| 
 | |
|         This event is fired only on the main process and **NOT** on any
 | |
|         worker processes. You should typically use this event to initialize
 | |
|         resources that are shared across workers, or to initialize resources
 | |
|         that are not safe to be initialized in a worker process.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.main_process_start
 | |
|             async def on_main_process_start(app: Sanic):
 | |
|                 print("Main process started")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "main_process_start")
 | |
| 
 | |
|     def main_process_ready(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the main_process_ready event.
 | |
| 
 | |
|         This event is fired only on the main process and **NOT** on any
 | |
|         worker processes. It is fired after the main process has started and
 | |
|         the Worker Manager has been initialized (ie, you will have access to
 | |
|         `app.manager` instance). The typical use case for this event is to
 | |
|         add a managed process to the Worker Manager.
 | |
| 
 | |
|         See [Running custom processes](/en/guide/deployment/manager.html#running-custom-processes) and [Listeners](/en/guide/basics/listeners.html) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.main_process_ready
 | |
|             async def on_main_process_ready(app: Sanic):
 | |
|                 print("Main process ready")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "main_process_ready")
 | |
| 
 | |
|     def main_process_stop(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the main_process_stop event.
 | |
| 
 | |
|         This event is fired only on the main process and **NOT** on any
 | |
|         worker processes. You should typically use this event to clean up
 | |
|         resources that were initialized in the main_process_start event.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.main_process_stop
 | |
|             async def on_main_process_stop(app: Sanic):
 | |
|                 print("Main process stopped")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "main_process_stop")
 | |
| 
 | |
|     def reload_process_start(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the reload_process_start event.
 | |
| 
 | |
|         This event is fired only on the reload process and **NOT** on any
 | |
|         worker processes. This is similar to the main_process_start event,
 | |
|         except that it is fired only when the reload process is started.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.reload_process_start
 | |
|             async def on_reload_process_start(app: Sanic):
 | |
|                 print("Reload process started")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "reload_process_start")
 | |
| 
 | |
|     def reload_process_stop(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the reload_process_stop event.
 | |
| 
 | |
|         This event is fired only on the reload process and **NOT** on any
 | |
|         worker processes. This is similar to the main_process_stop event,
 | |
|         except that it is fired only when the reload process is stopped.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.reload_process_stop
 | |
|             async def on_reload_process_stop(app: Sanic):
 | |
|                 print("Reload process stopped")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "reload_process_stop")
 | |
| 
 | |
|     def before_reload_trigger(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the before_reload_trigger event.
 | |
| 
 | |
|         This event is fired only on the reload process and **NOT** on any
 | |
|         worker processes. This event is fired before the reload process
 | |
|         triggers the reload. A change event has been detected and the reload
 | |
|         process is about to be triggered.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.before_reload_trigger
 | |
|             async def on_before_reload_trigger(app: Sanic):
 | |
|                 print("Before reload trigger")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "before_reload_trigger")
 | |
| 
 | |
|     def after_reload_trigger(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the after_reload_trigger event.
 | |
| 
 | |
|         This event is fired only on the reload process and **NOT** on any
 | |
|         worker processes. This event is fired after the reload process
 | |
|         triggers the reload. A change event has been detected and the reload
 | |
|         process has been triggered.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.after_reload_trigger
 | |
|             async def on_after_reload_trigger(app: Sanic, changed: set[str]):
 | |
|                 print("After reload trigger, changed files: ", changed)
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "after_reload_trigger")
 | |
| 
 | |
|     def before_server_start(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the before_server_start event.
 | |
| 
 | |
|         This event is fired on all worker processes. You should typically
 | |
|         use this event to initialize resources that are global in nature, or
 | |
|         will be shared across requests and various parts of the application.
 | |
| 
 | |
|         A common use case for this event is to initialize a database connection
 | |
|         pool, or to initialize a cache client.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.before_server_start
 | |
|             async def on_before_server_start(app: Sanic):
 | |
|                 print("Before server start")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "before_server_start")
 | |
| 
 | |
|     def after_server_start(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the after_server_start event.
 | |
| 
 | |
|         This event is fired on all worker processes. You should typically
 | |
|         use this event to run background tasks, or perform other actions that
 | |
|         are not directly related to handling requests. In theory, it is
 | |
|         possible that some requests may be handled before this event is fired,
 | |
|         so you should not use this event to initialize resources that are
 | |
|         required for handling requests.
 | |
| 
 | |
|         A common use case for this event is to start a background task that
 | |
|         periodically performs some action, such as clearing a cache or
 | |
|         performing a health check.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.after_server_start
 | |
|             async def on_after_server_start(app: Sanic):
 | |
|                 print("After server start")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "after_server_start")
 | |
| 
 | |
|     def before_server_stop(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the before_server_stop event.
 | |
| 
 | |
|         This event is fired on all worker processes. This event is fired
 | |
|         before the server starts shutting down. You should not use this event
 | |
|         to perform any actions that are required for handling requests, as
 | |
|         some requests may continue to be handled after this event is fired.
 | |
| 
 | |
|         A common use case for this event is to stop a background task that
 | |
|         was started in the after_server_start event.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.before_server_stop
 | |
|             async def on_before_server_stop(app: Sanic):
 | |
|                 print("Before server stop")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "before_server_stop")
 | |
| 
 | |
|     def after_server_stop(
 | |
|         self, listener: ListenerType[Sanic]
 | |
|     ) -> ListenerType[Sanic]:
 | |
|         """Decorator for registering a listener for the after_server_stop event.
 | |
| 
 | |
|         This event is fired on all worker processes. This event is fired
 | |
|         after the server has stopped shutting down, and all requests have
 | |
|         been handled. You should typically use this event to clean up
 | |
|         resources that were initialized in the before_server_start event.
 | |
| 
 | |
|         A common use case for this event is to close a database connection
 | |
|         pool, or to close a cache client.
 | |
| 
 | |
|         See [Listeners](/en/guide/basics/listeners) for more details.
 | |
| 
 | |
|         Args:
 | |
|             listener (ListenerType[Sanic]): The listener handler to attach.
 | |
| 
 | |
|         Examples:
 | |
|             ```python
 | |
|             @app.after_server_stop
 | |
|             async def on_after_server_stop(app: Sanic):
 | |
|                 print("After server stop")
 | |
|             ```
 | |
|         """  # noqa: E501
 | |
|         return self.listener(listener, "after_server_stop")
 | 
