File size: 6,696 Bytes
9273228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
::: currentmodule
asyncio
:::

# Runners

**Source code:** `Lib/asyncio/runners.py`{.interpreted-text role="source"}

This section outlines high-level asyncio primitives to run asyncio code.

They are built on top of an `event loop <asyncio-event-loop>`{.interpreted-text role="ref"} 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 *coro* in an asyncio event loop and return the result.

The argument can be any awaitable object.

This function runs the awaitable, 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 `asyncio-debug-mode`{.interpreted-text role="ref"} settings.

If *loop_factory* is not `None`, it is used to create a new event loop; otherwise `asyncio.new_event_loop`{.interpreted-text role="func"} 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 `asyncio.EventLoop`{.interpreted-text role="class"} 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 `loop.shutdown_default_executor`{.interpreted-text role="meth"}.
:::

::: versionchanged
3.10

*debug* is `None` by default to respect the global debug mode settings.
:::

::: versionchanged
3.12

Added *loop_factory* parameter.
:::

::: versionchanged
3.14

*coro* can be any awaitable object.
:::

:::: note
::: title
Note
:::

The `!asyncio`{.interpreted-text role="mod"} policy system is deprecated and will be removed in Python 3.16; from there on, an explicit *loop_factory* is needed to configure the event loop.
::::
::::::::::

## Runner context manager

:::::::::: {.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 `event
loop <asyncio-event-loop>`{.interpreted-text role="ref"} and `contextvars.Context`{.interpreted-text role="class"}.

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 `asyncio-debug-mode`{.interpreted-text role="ref"} 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 `asyncio.new_event_loop`{.interpreted-text role="func"} is used and set as current event loop with `asyncio.set_event_loop`{.interpreted-text role="func"} if *loop_factory* is `None`.

Basically, `asyncio.run`{.interpreted-text role="func"} 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)

Execute *coro* in the embedded event loop.

The argument can be any awaitable object.

If the argument is a coroutine, it is wrapped in a Task.

An optional keyword-only *context* argument allows specifying a custom `contextvars.Context`{.interpreted-text role="class"} for the code to run in. The runner\'s default context is used if context is `None`.

Returns the awaitable\'s result or raises an exception.

This function cannot be called when another asyncio event loop is running in the same thread.

::: versionchanged
3.14

*coro* can be any awaitable object.
:::
::::

::: method
close()

Close the runner.

Finalize asynchronous generators, shutdown default executor, close the event loop and release embedded `contextvars.Context`{.interpreted-text role="class"}.
:::

::: method
get_loop()

Return the event loop associated with the runner instance.
:::

:::: note
::: title
Note
:::

`Runner`{.interpreted-text role="class"} uses the lazy initialization strategy, its constructor doesn\'t initialize underlying low-level structures.

Embedded *loop* and *context* are created at the `with`{.interpreted-text role="keyword"} body entering or the first call of `run`{.interpreted-text role="meth"} or `get_loop`{.interpreted-text role="meth"}.
::::
::::::::::

## Handling Keyboard Interruption

::: versionadded
3.11
:::

When `signal.SIGINT`{.interpreted-text role="const"} is raised by `Ctrl-C`{.interpreted-text role="kbd"}, `KeyboardInterrupt`{.interpreted-text role="exc"} exception is raised in the main thread by default. However this doesn\'t work with `asyncio`{.interpreted-text role="mod"} because it can interrupt asyncio internals and can hang the program from exiting.

To mitigate this issue, `asyncio`{.interpreted-text role="mod"} handles `signal.SIGINT`{.interpreted-text role="const"} as follows:

1.  `asyncio.Runner.run`{.interpreted-text role="meth"} installs a custom `signal.SIGINT`{.interpreted-text role="const"} handler before any user code is executed and removes it when exiting from the function.
2.  The `~asyncio.Runner`{.interpreted-text role="class"} creates the main task for the passed coroutine for its execution.
3.  When `signal.SIGINT`{.interpreted-text role="const"} is raised by `Ctrl-C`{.interpreted-text role="kbd"}, the custom signal handler cancels the main task by calling `asyncio.Task.cancel`{.interpreted-text role="meth"} which raises `asyncio.CancelledError`{.interpreted-text role="exc"} 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, `asyncio.Runner.run`{.interpreted-text role="meth"} raises `KeyboardInterrupt`{.interpreted-text role="exc"}.
4.  A user could write a tight loop which cannot be interrupted by `asyncio.Task.cancel`{.interpreted-text role="meth"}, in which case the second following `Ctrl-C`{.interpreted-text role="kbd"} immediately raises the `KeyboardInterrupt`{.interpreted-text role="exc"} without cancelling the main task.