Runners

Source code:Lib/asyncio/runners.py

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

They are built on top of anevent loop with the aimto simplify async code usage for common wide-spread scenarios.

Running an asyncio Program

asyncio.run(coro,*,debug=None,loop_factory=None)

Execute thecoroutinecoro and return the result.

This function runs the passed coroutine, taking care ofmanaging the asyncio event loop,finalizing asynchronousgenerators, and closing the executor.

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

Ifdebug isTrue, the event loop will be run in debug mode.False disablesdebug mode explicitly.None is used to respect the globalDebug Mode settings.

Ifloop_factory is notNone, it is used to create a new event loop;otherwiseasyncio.new_event_loop() 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 useloop_factory to configure the event loop instead of policies.Passingasyncio.EventLoop allows running asyncio without thepolicy system.

The executor is given a timeout duration of 5 minutes to shutdown.If the executor hasn’t finished within that duration, a warning isemitted and the executor is closed.

Example:

asyncdefmain():awaitasyncio.sleep(1)print('hello')asyncio.run(main())

Added in version 3.7.

Changed in version 3.9:Updated to useloop.shutdown_default_executor().

Changed in version 3.10:debug isNone by default to respect the global debug mode settings.

Changed in version 3.12:Addedloop_factory parameter.

Runner context manager

classasyncio.Runner(*,debug=None,loop_factory=None)

A context manager that simplifiesmultiple async function calls in the samecontext.

Sometimes several top-level async functions should be called in the sameeventloop andcontextvars.Context.

Ifdebug isTrue, the event loop will be run in debug mode.False disablesdebug mode explicitly.None is used to respect the globalDebug Mode settings.

loop_factory could be used for overriding the loop creation.It is the responsibility of theloop_factory to set the created loop as thecurrent one. By defaultasyncio.new_event_loop() is used and set ascurrent event loop withasyncio.set_event_loop() ifloop_factory isNone.

Basically,asyncio.run() example can be rewritten with the runner usage:

asyncdefmain():awaitasyncio.sleep(1)print('hello')withasyncio.Runner()asrunner:runner.run(main())

Added in version 3.11.

run(coro,*,context=None)

Run acoroutinecoro in the embedded loop.

Return the coroutine’s result or raise its exception.

An optional keyword-onlycontext argument allows specifying acustomcontextvars.Context for thecoro to run in.The runner’s default context is used ifNone.

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

close()

Close the runner.

Finalize asynchronous generators, shutdown default executor, close the event loopand release embeddedcontextvars.Context.

get_loop()

Return the event loop associated with the runner instance.

Note

Runner uses the lazy initialization strategy, its constructor doesn’tinitialize underlying low-level structures.

Embeddedloop andcontext are created at thewith body enteringor the first call ofrun() orget_loop().

Handling Keyboard Interruption

Added in version 3.11.

Whensignal.SIGINT is raised byCtrl-C,KeyboardInterruptexception is raised in the main thread by default. However this doesn’t work withasyncio because it can interrupt asyncio internals and can hang the program fromexiting.

To mitigate this issue,asyncio handlessignal.SIGINT as follows:

  1. asyncio.Runner.run() installs a customsignal.SIGINT handler beforeany user code is executed and removes it when exiting from the function.

  2. TheRunner creates the main task for the passed coroutine for itsexecution.

  3. Whensignal.SIGINT is raised byCtrl-C, the custom signal handlercancels the main task by callingasyncio.Task.cancel() which raisesasyncio.CancelledError inside the main task. This causes the Python stackto unwind,try/except andtry/finally blocks can be used for resourcecleanup. After the main task is cancelled,asyncio.Runner.run() raisesKeyboardInterrupt.

  4. A user could write a tight loop which cannot be interrupted byasyncio.Task.cancel(), in which case the second followingCtrl-Cimmediately raises theKeyboardInterrupt without cancelling the main task.