Example #1
0
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())
Example #2
0
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()
Example #3
0
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())
Example #4
0
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()
Example #5
0
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()
Example #6
0
    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()
Example #7
0
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()
Example #8
0
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()
Example #9
0
File: bot.py Project: Reliku/hikari
    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())
Example #11
0
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)
Example #12
0
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")
Example #13
0
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())