Sling Academy
Home/Python/Python asyncio.Runner() context manager (with examples)

Python asyncio.Runner() context manager (with examples)

Last updated: July 11, 2023

The Fundamentals

An asyncio.Runner() context manager is a new feature in Python 3.11 that simplifies running multiple async functions in the same context. It takes care of creating and closing an asyncio event loop, as well as managing the contextvars.Context for the async functions.

To use it, you need to create an instance of asyncio.Runner() with the with statement, and then call its run() method with the async function you want to execute. You can also pass a custom context to the run() method if you want to use a different contextvars.Context for the async function. The runner will return the result or raise the exception of the async function.

Here is an example of using asyncio.Runner() to run two async functions that print some messages with delays:

import asyncio

async def first():
  await asyncio.sleep(1)
  print("Welcome to Sling Academy!")

async def second():
  await asyncio.sleep(2)
  print("Have a nice day and have fun with Python!")

with asyncio.Runner() as runner:
  runner.run(first())
  runner.run(second())

This will output two messages (the second one will appear two seconds after the first one):

Welcome to Sling Academy!
Have a nice day and have fun with Python!

The close() method of asyncio.Runner() is used to close the runner and release its resources. It will finalize any asynchronous generators, shut down the default executor, close the event loop, and release the embedded contextvars.Context. You should call this method when you are done using the runner or use the with statement (like the example above) to automatically close it at the end of the block.

Advanced example

The example in the preceding section is basic, and you can get bored with it. Let’s examine a little bit more advanced program.

This example creates two coroutines that perform different tasks: one prints the current time every second for 10 seconds, and the other prints a message after 5 seconds. It then uses an asyncio runner to run both coroutines in the same event loop, one after the other. This demonstrates how to use the asyncio runner to execute multiple coroutines without creating a wrapper coroutine or multiple event loops.

import asyncio
import time

# A coroutine that prints the current time
async def print_time():
    print(time.strftime("%X"))

# A coroutine that waits for a given number of seconds
async def wait(seconds):
    await asyncio.sleep(seconds)

# A coroutine that prints the current time every second for 10 seconds
async def print_time_every_second():
    for _ in range(10):
        await print_time()
        await wait(1)

# A coroutine that prints a message after 5 seconds
async def print_message():
    await wait(5)
    print("Welcome to Sling Academy!")

# Create an asyncio runner
with asyncio.Runner() as runner:
    # Run the first coroutine
    runner.run(print_time_every_second())
    # Run the second coroutine
    runner.run(print_message())

Output (it depends on when you run the code):

22:34:23
22:34:24
22:34:25
22:34:26
22:34:27
22:34:28
22:34:29
22:34:30
22:34:31
22:34:32
Welcome to Sling Academy!

This example uses the context manager interface of the asyncio.Runner() class, which will close the event loop for us when we’re finished with it. It also uses two coroutines: print_time_every_second() and print_message(), which are executed in the same event loop by calling the run() method of the runner.

The difference between asyncio.Runner() and asyncio.run()

The asyncio.Runner() context manager is different from asyncio.run() function in several ways. The asyncio.run() function always creates a new event loop and closes it at the end, and it can only run one async function at a time. It is meant to be used as a main entry point for asyncio programs and, ideally, only be called once.

The asyncio.Runner() context manager allows you to reuse the same event loop and context for multiple async functions, and it also lets you pass a custom context to each async function if you want. It is meant to be used for scenarios where you need to run several top-level async functions in the same context, and it can be nested inside other async functions or contexts.

Next Article: Python: Running a function periodically with asyncio

Previous Article: Python: How to Define and Call Asynchronous Functions

Series: Python Asynchronous Programming Tutorials

Python

You May Also Like

  • Introduction to yfinance: Fetching Historical Stock Data in Python
  • Monitoring Volatility and Daily Averages Using cryptocompare
  • Advanced DOM Interactions: XPath and CSS Selectors in Playwright (Python)
  • Automating Strategy Updates and Version Control in freqtrade
  • Setting Up a freqtrade Dashboard for Real-Time Monitoring
  • Deploying freqtrade on a Cloud Server or Docker Environment
  • Optimizing Strategy Parameters with freqtrade’s Hyperopt
  • Risk Management: Setting Stop Loss, Trailing Stops, and ROI in freqtrade
  • Integrating freqtrade with TA-Lib and pandas-ta Indicators
  • Handling Multiple Pairs and Portfolios with freqtrade
  • Using freqtrade’s Backtesting and Hyperopt Modules
  • Developing Custom Trading Strategies for freqtrade
  • Debugging Common freqtrade Errors: Exchange Connectivity and More
  • Configuring freqtrade Bot Settings and Strategy Parameters
  • Installing freqtrade for Automated Crypto Trading in Python
  • Scaling cryptofeed for High-Frequency Trading Environments
  • Building a Real-Time Market Dashboard Using cryptofeed in Python
  • Customizing cryptofeed Callbacks for Advanced Market Insights
  • Integrating cryptofeed into Automated Trading Bots