.. currentmodule:: asyncio ======= Runners ======= **Source code:** :source:`Lib/asyncio/runners.py` This section outlines high-level asyncio primitives to run asyncio code. They are built on top of an :ref:`event loop ` with the aim to simplify async code usage for common wide-spread scenarios. .. contents:: :depth: 1 :local: Running an asyncio Program ========================== .. function:: run(coro, *, debug=None, loop_factory=None) Execute the :term:`coroutine` *coro* and return the result. This function runs the passed coroutine, taking care of managing the asyncio event loop, *finalizing asynchronous generators*, and closing the executor. This function cannot be called when another asyncio event loop is running in the same thread. If *debug* is ``True``, the event loop will be run in debug mode. ``False`` disables debug mode explicitly. ``None`` is used to respect the global :ref:`asyncio-debug-mode` settings. If *loop_factory* is not ``None``, it is used to create a new event loop; otherwise :func:`asyncio.new_event_loop` is used. The loop is closed at the end. This function should be used as a main entry point for asyncio programs, and should ideally only be called once. It is recommended to use *loop_factory* to configure the event loop instead of policies. Passing :class:`asyncio.EventLoop` allows running asyncio without the policy system. The executor is given a timeout duration of 5 minutes to shutdown. If the executor hasn't finished within that duration, a warning is emitted and the executor is closed. Example:: async def main(): await asyncio.sleep(1) print('hello') asyncio.run(main()) .. versionadded:: 3.7 .. versionchanged:: 3.9 Updated to use :meth:`loop.shutdown_default_executor`. .. versionchanged:: 3.10 *debug* is ``None`` by default to respect the global debug mode settings. .. versionchanged:: 3.12 Added *loop_factory* parameter. Runner context manager ====================== .. class:: Runner(*, debug=None, loop_factory=None) A context manager that simplifies *multiple* async function calls in the same context. Sometimes several top-level async functions should be called in the same :ref:`event loop ` and :class:`contextvars.Context`. If *debug* is ``True``, the event loop will be run in debug mode. ``False`` disables debug mode explicitly. ``None`` is used to respect the global :ref:`asyncio-debug-mode` settings. *loop_factory* could be used for overriding the loop creation. It is the responsibility of the *loop_factory* to set the created loop as the current one. By default :func:`asyncio.new_event_loop` is used and set as current event loop with :func:`asyncio.set_event_loop` if *loop_factory* is ``None``. Basically, :func:`asyncio.run()` example can be rewritten with the runner usage:: async def main(): await asyncio.sleep(1) print('hello') with asyncio.Runner() as runner: runner.run(main()) .. versionadded:: 3.11 .. method:: run(coro, *, context=None) Run a :term:`coroutine ` *coro* in the embedded loop. Return the coroutine's result or raise its exception. An optional keyword-only *context* argument allows specifying a custom :class:`contextvars.Context` for the *coro* to run in. The runner's default context is used if ``None``. This function cannot be called when another asyncio event loop is running in the same thread. .. method:: close() Close the runner. Finalize asynchronous generators, shutdown default executor, close the event loop and release embedded :class:`contextvars.Context`. .. method:: get_loop() Return the event loop associated with the runner instance. .. note:: :class:`Runner` uses the lazy initialization strategy, its constructor doesn't initialize underlying low-level structures. Embedded *loop* and *context* are created at the :keyword:`with` body entering or the first call of :meth:`run` or :meth:`get_loop`. Handling Keyboard Interruption ============================== .. versionadded:: 3.11 When :const:`signal.SIGINT` is raised by :kbd:`Ctrl-C`, :exc:`KeyboardInterrupt` exception is raised in the main thread by default. However this doesn't work with :mod:`asyncio` because it can interrupt asyncio internals and can hang the program from exiting. To mitigate this issue, :mod:`asyncio` handles :const:`signal.SIGINT` as follows: 1. :meth:`asyncio.Runner.run` installs a custom :const:`signal.SIGINT` handler before any user code is executed and removes it when exiting from the function. 2. The :class:`~asyncio.Runner` creates the main task for the passed coroutine for its execution. 3. When :const:`signal.SIGINT` is raised by :kbd:`Ctrl-C`, the custom signal handler cancels the main task by calling :meth:`asyncio.Task.cancel` which raises :exc:`asyncio.CancelledError` inside the main task. This causes the Python stack to unwind, ``try/except`` and ``try/finally`` blocks can be used for resource cleanup. After the main task is cancelled, :meth:`asyncio.Runner.run` raises :exc:`KeyboardInterrupt`. 4. A user could write a tight loop which cannot be interrupted by :meth:`asyncio.Task.cancel`, in which case the second following :kbd:`Ctrl-C` immediately raises the :exc:`KeyboardInterrupt` without cancelling the main task.