PyComp / python_doc_md /Intermediate /library /asyncio-runner.md
ITookAPill's picture
PyComp First Commit
9273228
|
Raw
History Blame Contribute Delete
6.7 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

::: 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.