mirror of https://github.com/python/cpython
155 lines
5.1 KiB
ReStructuredText
155 lines
5.1 KiB
ReStructuredText
.. 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 <asyncio-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)
|
|
|
|
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.
|
|
|
|
This function always creates a new event loop and closes it at
|
|
the end. It should be used as a main entry point for asyncio
|
|
programs, and should ideally only be called once.
|
|
|
|
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.
|
|
|
|
|
|
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 <asyncio-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 <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.
|