def _shutdown_tasks(loop: asyncio.AbstractEventLoop) -> None: """Завершение в случае ошибки. После ошибки происходит отмена всех заданий, чтобы не захламлять сообщение об ошибке множеством сообщений, о том, что результат выполнения задания не был awaited. Идея кода позаимствована из реализации asyncio.app. """ to_cancel = asyncio.all_tasks(loop) if not to_cancel: return for task in to_cancel: task.cancel() loop.run_until_complete(asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)) for canceled_task in to_cancel: if canceled_task.cancelled(): continue if canceled_task.exception() is not None: loop.call_exception_handler( { "message": "unhandled EventBus exception", "exception": canceled_task.exception(), "task": canceled_task, }, ) loop.run_until_complete(loop.shutdown_asyncgens()) loop.run_until_complete(loop.shutdown_default_executor())
def _cleanup_loop(loop: asyncio.AbstractEventLoop) -> None: try: _cancel_tasks(loop) loop.run_until_complete(loop.shutdown_asyncgens()) finally: log.info('Closing the event loop.') loop.close()
def loop_mgr(loop: asyncio.AbstractEventLoop): """ An asyncio loop manager, used by :py:class:`.AsyncioExecutor` to run the loop forever in a thread and clean up after the loop stops. :param loop: """ try: # loop manager will run this in it's own thread loop.run_forever() # the loop was stopped and concurrent.futures.Executor # promises to complete tasks on shutdown. while True: tasks = asyncio.all_tasks(loop=loop) pending = [t for t in tasks if not t.done()] loop.run_until_complete(asyncio.gather(*pending)) # ensure the task collection is updated # (this is _not_ redundant) tasks = asyncio.all_tasks(loop=loop) if all([t.done() for t in tasks]): break finally: loop.run_until_complete(loop.shutdown_asyncgens())
def shutdown_loop(loop: asyncio.AbstractEventLoop) -> None: if loop.is_closed(): return try: loop.stop() cancel_all_tasks(loop) loop.run_until_complete(loop.shutdown_asyncgens()) finally: loop.close()
def run(loop: asyncio.AbstractEventLoop, arguments: dict): try: loop.run_until_complete(main(arguments)) loop.run_until_complete(asyncio.sleep(0.250)) loop.run_until_complete(loop.shutdown_asyncgens()) except Exception: log.exception("there has been an unrecoverable error") finally: loop.close()
def start_async_callback( event_loop: asyncio.AbstractEventLoop, coroutine_func, shutdown_event: Event, ): asyncio.set_event_loop(event_loop) try: event_loop.run_until_complete(coroutine_func()) event_loop.run_until_complete(event_loop.shutdown_asyncgens()) event_loop.close() finally: shutdown_event.set()
def shutdown_loop(loop: asyncio.AbstractEventLoop) -> None: if loop.is_closed(): return try: cancel_all_tasks(loop) loop.run_until_complete(loop.shutdown_asyncgens()) except Exception as exc: log.warning(f"Error<{type(exc).__name__}> was raised. {exc}") finally: loop.close()
def shutdown_loop(loop: asyncio.AbstractEventLoop) -> None: if loop.is_closed(): return try: loop.stop() cancel_all_tasks(loop) loop.run_until_complete(loop.shutdown_asyncgens()) except RuntimeError as error: log.warning("Error while shutting loop down: %s", str(error)) finally: loop.close()
def _destroy_loop(loop: asyncio.AbstractEventLoop) -> None: async def murder(future: asyncio.Future[typing.Any]) -> None: # These include _GatheringFuture which must be awaited if the children # throw an asyncio.CancelledError, otherwise it will spam logs with warnings # about exceptions not being retrieved before GC. try: _LOGGER.log(ux.TRACE, "killing %s", future) future.cancel() await future except asyncio.CancelledError: pass except Exception as ex: loop.call_exception_handler({ "message": "Future raised unexpected exception after requesting cancellation", "exception": ex, "future": future, }) remaining_tasks = [ t for t in asyncio.all_tasks(loop) if not t.cancelled() and not t.done() ] if remaining_tasks: _LOGGER.debug("terminating %s remaining tasks forcefully", len(remaining_tasks)) loop.run_until_complete( asyncio.gather(*(murder(task) for task in remaining_tasks))) else: _LOGGER.debug("No remaining tasks exist, good job!") if sys.version_info >= (3, 9): _LOGGER.debug("shutting down default executor") with contextlib.suppress(NotImplementedError): # This seems to raise a NotImplementedError when running with uvloop. loop.run_until_complete(loop.shutdown_default_executor()) _LOGGER.debug("shutting down asyncgens") loop.run_until_complete(loop.shutdown_asyncgens()) _LOGGER.debug("closing event loop") loop.close()
def _loop_mgr(loop: asyncio.AbstractEventLoop) -> None: """起一个线程执行事件循环的`run_forever`方法. [Start up a thread for running the eventloop's `run_forever` method.] 当它被终止时会清理未完结的协程,但不会关闭事件循环 [When it shutdown, all the coroutines will be closed.but the eventloop will not close.] Params: loop (asyncio.AbstractEventLoop) : - 事件循环[the eventloop] """ if loop.is_closed(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: loop.run_forever() finally: loop.run_until_complete(loop.shutdown_asyncgens())
def run_single( app: Type[ASGIFramework], config: Config, *, loop: asyncio.AbstractEventLoop, sock: Optional[socket] = None, is_child: bool = False, ) -> None: """Create a server to run the app on given the options. Arguments: app: The ASGI Framework to run. config: The configuration that defines the server. loop: Asyncio loop to create the server in, if None, take default one. """ if loop is None: warnings.warn( 'Event loop is not specified, this can cause unexpected errors') loop = asyncio.get_event_loop() if config.pid_path is not None and not is_child: _write_pid_file(config.pid_path) loop.set_debug(config.debug) if hasattr(app, 'startup'): loop.run_until_complete(app.startup()) # type: ignore if sock is not None: create_server = loop.create_server( lambda: Server(app, loop, config), ssl=config.ssl, sock=sock, reuse_port=is_child, ) elif config.file_descriptor is not None: sock = socket_fromfd(config.file_descriptor, AF_UNIX, SOCK_STREAM) create_server = loop.create_server( lambda: Server(app, loop, config), ssl=config.ssl, sock=sock, ) elif config.unix_domain is not None: create_server = loop.create_unix_server( lambda: Server(app, loop, config), config.unix_domain, ssl=config.ssl, ) else: create_server = loop.create_server( lambda: Server(app, loop, config), host=config.host, port=config.port, ssl=config.ssl, reuse_port=is_child, ) server = loop.run_until_complete(create_server) if platform.system() == 'Windows': loop.create_task(_windows_signal_support()) try: loop.add_signal_handler(signal.SIGINT, _raise_shutdown) loop.add_signal_handler(signal.SIGTERM, _raise_shutdown) except NotImplementedError: pass # Unix only reload_ = False try: if config.use_reloader: loop.run_until_complete(_observe_changes()) reload_ = True else: loop.run_forever() except (SystemExit, KeyboardInterrupt): pass finally: server.close() loop.run_until_complete(server.wait_closed()) loop.run_until_complete(loop.shutdown_asyncgens()) try: loop.remove_signal_handler(signal.SIGINT) loop.remove_signal_handler(signal.SIGTERM) except NotImplementedError: pass # Unix only if hasattr(app, 'cleanup'): loop.run_until_complete(app.cleanup()) # type: ignore loop.close() if reload_: # Restart this process (only safe for dev/debug) os.execv(sys.executable, [sys.executable] + sys.argv)
def run( func: Optional[Awaitable[None]] = None, *, finalize: Optional[Awaitable[None]] = None, loop: AbstractEventLoop = None, ): """ Configure the event loop to react to signals and exceptions then run the provided coroutine and loop forever. Shutdown the event loop when a signal or exception is received or the supplied function explicitly requests the loop to stop. This function provides some of the common boilerplate typically needed when running asyncio applications. It registers signal handlers that listen for SIGINT and SIGTERM that will stop the event loop and trigger application shutdown actions. It registers a global exception handler that will stop the loop and trigger application shutdown actions. This helps catch a common problem that occurs in asyncio applications in which an exception occurs in a task that was spun off but is not reported until the event loop is stopped. This approach allows users to be notified about these issues as soon as possible. :param func: A coroutine to run before looping forever. This coroutine is typically the "main" coroutine from which all other work is spawned. The event loop will continue to run after the supplied coroutine completes. :param finalize: An optional coroutine to run when shutting down. Use this to perform any graceful cleanup activities such as finalising log files, disconnecting from services such as databases, etc. :param loop: An optional event loop to run. If not supplied the default event loop is used (i.e., whatever ``asyncio.get_event_loop()`` returns. """ logger.debug("Application runner starting") if func: if not (inspect.isawaitable(func) or inspect.iscoroutinefunction(func)): raise Exception("func must be a coroutine or a coroutine function " f"that takes no arguments, got {func}") if finalize: if not (inspect.isawaitable(finalize) or inspect.iscoroutinefunction(finalize)): raise Exception( "finalize must be a coroutine or a coroutine function " f"that takes no arguments, got {finalize}") # Use a supplied loop or the default event loop. If the loop is closed # (which can happen in unit tests) then create a new event loop. loop = loop or asyncio.get_event_loop() if loop.is_closed(): loop = asyncio.new_event_loop() def signal_handler(loop, sig): logger.info(f"Caught {sig.name}, stopping.") loop.call_soon(loop.stop) loop.add_signal_handler(SIGINT, signal_handler, loop, SIGINT) loop.add_signal_handler(SIGTERM, signal_handler, loop, SIGTERM) def exception_handler(loop, context): exc_msg = context["message"] exc = context["exception"] logger.exception(f"Caught exception: {exc_msg}", exc_info=exc) loop.call_soon(loop.stop) loop.set_exception_handler(exception_handler) try: if func: if inspect.iscoroutinefunction(func): func = func() # type: ignore assert func is not None loop.create_task(func) loop.run_forever() finally: logger.debug("Application shutdown sequence starting") if finalize: if inspect.iscoroutinefunction(finalize): finalize = finalize() # type: ignore assert finalize is not None loop.run_until_complete(finalize) # Shutdown any outstanding tasks that are left running pending_tasks = all_tasks(loop=loop) if pending_tasks: logger.debug(f"Cancelling {len(pending_tasks)} pending tasks.") for task in pending_tasks: logger.debug(f"Cancelling task: {task}") task.cancel() try: loop.run_until_complete(asyncio.gather(*pending_tasks)) except asyncio.CancelledError: pass loop.run_until_complete(loop.shutdown_asyncgens()) logger.debug("Application shutdown sequence complete") loop.close() logger.debug("Application runner stopped")
def entrypoint(*services: Service, loop: asyncio.AbstractEventLoop = None, pool_size: int = None, log_level: Union[int, str] = logging.INFO, log_format: Union[str, LogFormat] = 'color', log_buffer_size: int = 1024, log_flush_interval: float = 0.2, log_config: bool = True): """ :param loop: loop :param services: Service instances which will be starting. :param pool_size: thread pool size :param log_level: Logging level which will be configured :param log_format: Logging format which will be configures :param log_buffer_size: Buffer size for logging :param log_flush_interval: interval in seconds for flushing logs :param log_config: if False do not configure logging """ loop = loop or new_event_loop(pool_size) if log_config: basic_config( level=log_level, log_format=log_format, buffered=False, ) async def start(): nonlocal loop nonlocal services nonlocal log_format nonlocal log_level nonlocal log_buffer_size nonlocal log_flush_interval if log_config: basic_config( level=log_level, log_format=log_format, buffered=True, loop=loop, buffer_size=log_buffer_size, flush_interval=log_flush_interval, ) starting = [] for svc in services: svc.set_loop(loop) starting.append(loop.create_task(svc.start())) await asyncio.gather(*starting, loop=loop) loop.run_until_complete(start()) shutting_down = False try: yield loop except Exception as e: graceful_shutdown(services, loop, e) shutting_down = True raise finally: if not shutting_down: graceful_shutdown(services, loop, None) loop.run_until_complete(loop.shutdown_asyncgens())