1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
.. 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 threadpool.
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.
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.
|