1.. currentmodule:: asyncio 2 3 4======= 5Runners 6======= 7 8**Source code:** :source:`Lib/asyncio/runners.py` 9 10 11This section outlines high-level asyncio primitives to run asyncio code. 12 13They are built on top of an :ref:`event loop <asyncio-event-loop>` with the aim 14to simplify async code usage for common wide-spread scenarios. 15 16.. contents:: 17 :depth: 1 18 :local: 19 20 21 22Running an asyncio Program 23========================== 24 25.. function:: run(coro, *, debug=None) 26 27 Execute the :term:`coroutine` *coro* and return the result. 28 29 This function runs the passed coroutine, taking care of 30 managing the asyncio event loop, *finalizing asynchronous 31 generators*, and closing the threadpool. 32 33 This function cannot be called when another asyncio event loop is 34 running in the same thread. 35 36 If *debug* is ``True``, the event loop will be run in debug mode. ``False`` disables 37 debug mode explicitly. ``None`` is used to respect the global 38 :ref:`asyncio-debug-mode` settings. 39 40 This function always creates a new event loop and closes it at 41 the end. It should be used as a main entry point for asyncio 42 programs, and should ideally only be called once. 43 44 Example:: 45 46 async def main(): 47 await asyncio.sleep(1) 48 print('hello') 49 50 asyncio.run(main()) 51 52 .. versionadded:: 3.7 53 54 .. versionchanged:: 3.9 55 Updated to use :meth:`loop.shutdown_default_executor`. 56 57 .. versionchanged:: 3.10 58 59 *debug* is ``None`` by default to respect the global debug mode settings. 60 61 62Runner context manager 63====================== 64 65.. class:: Runner(*, debug=None, loop_factory=None) 66 67 A context manager that simplifies *multiple* async function calls in the same 68 context. 69 70 Sometimes several top-level async functions should be called in the same :ref:`event 71 loop <asyncio-event-loop>` and :class:`contextvars.Context`. 72 73 If *debug* is ``True``, the event loop will be run in debug mode. ``False`` disables 74 debug mode explicitly. ``None`` is used to respect the global 75 :ref:`asyncio-debug-mode` settings. 76 77 *loop_factory* could be used for overriding the loop creation. 78 It is the responsibility of the *loop_factory* to set the created loop as the 79 current one. By default :func:`asyncio.new_event_loop` is used and set as 80 current event loop with :func:`asyncio.set_event_loop` if *loop_factory* is ``None``. 81 82 Basically, :func:`asyncio.run()` example can be rewritten with the runner usage:: 83 84 async def main(): 85 await asyncio.sleep(1) 86 print('hello') 87 88 with asyncio.Runner() as runner: 89 runner.run(main()) 90 91 .. versionadded:: 3.11 92 93 .. method:: run(coro, *, context=None) 94 95 Run a :term:`coroutine <coroutine>` *coro* in the embedded loop. 96 97 Return the coroutine's result or raise its exception. 98 99 An optional keyword-only *context* argument allows specifying a 100 custom :class:`contextvars.Context` for the *coro* to run in. 101 The runner's default context is used if ``None``. 102 103 This function cannot be called when another asyncio event loop is 104 running in the same thread. 105 106 .. method:: close() 107 108 Close the runner. 109 110 Finalize asynchronous generators, shutdown default executor, close the event loop 111 and release embedded :class:`contextvars.Context`. 112 113 .. method:: get_loop() 114 115 Return the event loop associated with the runner instance. 116 117 .. note:: 118 119 :class:`Runner` uses the lazy initialization strategy, its constructor doesn't 120 initialize underlying low-level structures. 121 122 Embedded *loop* and *context* are created at the :keyword:`with` body entering 123 or the first call of :meth:`run` or :meth:`get_loop`. 124 125 126Handling Keyboard Interruption 127============================== 128 129.. versionadded:: 3.11 130 131When :const:`signal.SIGINT` is raised by :kbd:`Ctrl-C`, :exc:`KeyboardInterrupt` 132exception is raised in the main thread by default. However this doesn't work with 133:mod:`asyncio` because it can interrupt asyncio internals and can hang the program from 134exiting. 135 136To mitigate this issue, :mod:`asyncio` handles :const:`signal.SIGINT` as follows: 137 1381. :meth:`asyncio.Runner.run` installs a custom :const:`signal.SIGINT` handler before 139 any user code is executed and removes it when exiting from the function. 1402. The :class:`~asyncio.Runner` creates the main task for the passed coroutine for its 141 execution. 1423. When :const:`signal.SIGINT` is raised by :kbd:`Ctrl-C`, the custom signal handler 143 cancels the main task by calling :meth:`asyncio.Task.cancel` which raises 144 :exc:`asyncio.CancelledError` inside the main task. This causes the Python stack 145 to unwind, ``try/except`` and ``try/finally`` blocks can be used for resource 146 cleanup. After the main task is cancelled, :meth:`asyncio.Runner.run` raises 147 :exc:`KeyboardInterrupt`. 1484. A user could write a tight loop which cannot be interrupted by 149 :meth:`asyncio.Task.cancel`, in which case the second following :kbd:`Ctrl-C` 150 immediately raises the :exc:`KeyboardInterrupt` without cancelling the main task. 151