You can configure, suppress, and log warnings emitted by asyncio programs.
By default, the asyncio event loop will emit warnings in situations that are not harmful to the running program but should be inspected and changed by the developer. More warnings can be enabled in the programs, and we can choose to filter out and suppress all warnings if needed.
A best practice in a production system is to log all warnings so that they are not lost and instead can be inspected and addressed.
In this tutorial, you will discover how to configure, suppress, and log warnings in asyncio programs.
Let’s get started.
What Are Warnings
Python will emit warnings when something non-normal has occurred and the user or program should know about it.
Warnings are different from an exception as they report a condition rather than terminate a task or program.
Warning messages are typically issued in situations where it is useful to alert the user of some condition in a program, where that condition (normally) doesn’t warrant raising an exception and terminating the program.
— warnings — Warning control
The “warnings” module provides the ability to configure the types of warnings that may be reported and allows warnings to be ignored.
Warning messages are normally written to sys.stderr, but their disposition can be changed flexibly, from ignoring all warnings to turning them into exceptions.
— warnings — Warning control
By default, warning messages raised in a program are reported to standard error (stderr).
Many modules in the Python standard library can raise warnings, including the asyncio module.
Warnings are raised in a program by a call to the warnings.warn() module function.
These messages signal to the application developer about a situation that is not critical, but should be changed by updating the program.
Issue a warning, or maybe ignore it or raise an exception.
— warnings — Warning control
Run loops using all CPUs, download your FREE book to learn how.
Asyncio Raises Warnings
The asyncio module will emit warnings in some situations.
Perhaps the most common warnings emitted by the asyncio module are when coroutines are created but not awaited.
This can raise two RuntimeWarnings, they are:
- RuntimeWarning: coroutine ‘…’ was never awaited
- RuntimeWarning: Enable tracemalloc to get the object allocation traceback
You can learn more about this type of warning in the tutorial:
Additional details are provided in asyncio warning messages when running the event loop in debug mode.
This will include a stack trace of coroutines that are created and not run and traces of exceptions that are raised in tasks and not retrieved.
You can learn more about running the asyncio event loop in debug mode in the tutorial:
The warnings module in the Python standard library provides some ability to filter warnings and configure how they are handled by the program.
This can be done from the command line (e.g. -W), from an environment variable (e.g. PYTHONWARNINGS) or programmatically via the warnings.filterwarnings() module function.
We will use this latter approach in this tutorial for simplicity.
How to Enable More Asyncio Warnings
The asyncio module emits RuntimeWarnings that will be reported by default.
It may also emit ResourceWarning warnings.
These can be configured to always be shown by calling the warnings.filterwarnings() module function and specifying to ‘always‘ show warnings of the type ResourceWarning.
For example:
1 2 3 |
... # always show ResourceWarning warnings warnings.filterwarnings('always', category=ResourceWarning) |
This call can be made directly after importing the “warnings” module, a common practice.
For example:
1 2 3 4 |
... import warnings # always show ResourceWarning warnings warnings.filterwarnings('always', category=ResourceWarning) |
Alternatively, this can be done before starting the asyncio event loop.
For example:
1 2 3 4 5 |
... # always show ResourceWarning warnings warnings.filterwarnings('always', category=ResourceWarning) # start the event loop asyncio.run(main()) |
We can be more aggressive and ensure that all warnings are always shown.
This can be achieved by calling the warnings.filterwarnings() module function and specifying to ‘always‘ show warnings and not specifying a type of warning to show.
This will show all warnings.
1 2 3 |
... # show all warnings warnings.filterwarnings('always') |
Additionally, we can convert all warnings into exceptions.
This can terminate tasks and even the event loop and may be desirable when debugging an asyncio program.
We can achieve this by calling the warnings.filterwarnings() module function and specify ‘error‘ when warnings are emitted.
For example:
1 2 3 |
... # all warnings will raise an exception warnings.filterwarnings('error') |
How to Suppress Asyncio Warnings
By default, RuntimeWarning warnings are reported.
These warnings can be suppressed by calling the warnings.filterwarnings() module function and specifying to ‘ignore‘ warnings of the type RuntimeWarning.
For example:
1 2 3 |
... # ignore RuntimeWarning from asyncio warnings.filterwarnings('ignore', category=RuntimeWarning) |
This call can be made directly after importing the warnings module, a common practice.
For example:
1 2 3 4 |
... import warnings # ignore RuntimeWarning from asyncio warnings.filterwarnings('ignore', category=RuntimeWarning) |
Alternatively, this can be done before starting the asyncio event loop.
For example:
1 2 3 4 5 |
... # ignore RuntimeWarning from asyncio warnings.filterwarnings('ignore', category=RuntimeWarning) # start the event loop asyncio.run(main()) |
A more aggressive approach may be to suppress all warnings, not just RuntimeWarning.
This can be achieved by calling the warnings.filterwarnings() module function and specifying to ‘ignore‘ warnings and not specifying a type of warning to ignore.
This will ignore all warnings.
For example:
1 2 3 |
... # suppress all warnings warnings.filterwarnings('ignore') |
Because warnings signal a problem with the program, it is generally not a good idea to suppress them.
How to Log Warnings
We may use logging in our asyncio program via the “logging” module.
We can configure the logging infrastructure to log warnings raised in our program.
This can be achieved by calling the logging.captureWarnings() function and passing the argument True.
This function is used to turn the capture of warnings by logging on and off.
— logging — Logging facility for Python
For example:
1 2 3 |
... # turn warnings into WARNING level log message. logging.captureWarnings(True) |
This will have all warning non-ignored warning messages transformed into log messages with the level severity WARNING.
It is very helpful to convert warnings into WARNING-level log messages, for example:
- It ensures warnings are treated seriously during development and in production.
- It ensures that the warnings are added to the main application log, along with other messages.
- It ensures the warnings are not lost in a running system.
Now that we know about warnings in asyncio, let’s look at some worked examples.
Example of Reporting Asyncio Warnings
We can explore an example of asyncio emitting a RuntimeWarning.
In this example, we will define a new coroutine that sleeps for a moment. The main() coroutine will then create this new coroutine, but will not await it. This will cause the asyncio event loop to emit a RuntimeWarning.
To be sure we see the RuntimeWarning, we will configure the warnings infrastructure to always show all warnings.
1 2 3 |
... # emit all warnings warnings.filterwarnings('always') |
This is not needed, but it is good practice for us.
Firstly, we can define the coroutine that sleeps a moment.
The work() coroutine below implements this.
1 2 3 4 |
# task that does work async def work(): # block for a moment await asyncio.sleep(1) |
Next, we can define the main() coroutine that creates the work() coroutine but does not await it. It then sleeps for two seconds and reports a done message.
1 2 3 4 5 6 7 8 |
# main coroutine async def main(): # create coroutine, but never run work() # block for a moment await asyncio.sleep(2) # report a message print('Done') |
Finally, we can start the asyncio event loop.
1 2 3 |
... # start the event loop asyncio.run(main()) |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# SuperFastPython.com # example of always showing warnings in asyncio import warnings # emit all warnings warnings.filterwarnings('always') import asyncio # task that does work async def work(): # block for a moment await asyncio.sleep(1) # main coroutine async def main(): # create coroutine, but never run work() # block for a moment await asyncio.sleep(2) # report a message print('Done') # start the event loop asyncio.run(main()) |
Running the example first starts the asyncio event loop and runs the main() coroutine.
The main() coroutine runs and creates the work() coroutine but does not run it.
The program then emits two RuntimeWarning warnings.
The first warning highlights that the created work() coroutine was never awaited.
The second suggests enabling tracemalloc to help discover where the coroutine was created and not awaited.
Finally, the main() coroutine sleeps for two seconds and reports a done message.
This example highlights how asyncio programs will emit warnings when we take actions in our program that are not harmful, but probably signal a problem.
1 2 3 4 |
...: RuntimeWarning: coroutine 'work' was never awaited work() RuntimeWarning: Enable tracemalloc to get the object allocation traceback Done |
Next, let’s explore an example of suppressing warnings in asyncio.
Free Python Asyncio Course
Download your FREE Asyncio PDF cheat sheet and get BONUS access to my free 7-day crash course on the Asyncio API.
Discover how to use the Python asyncio module including how to define, create, and run new coroutines and how to use non-blocking I/O.
Example of Suppressing Asyncio Warnings
We can explore an example of suppressing warnings in asyncio.
In this case, we can update the above example to suppress all warnings.
This can be achieved by configuring the warnings.filterwarnings() with ‘ignore‘.
1 2 3 |
... # emit no warnings warnings.filterwarnings('ignore') |
And that’s it.
Given that we know the example emits warnings and that we will actively suppress them, we do not expect to see any warning messages.
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# SuperFastPython.com # example of suppressing all warnings in asyncio import warnings # emit no warnings warnings.filterwarnings('ignore') import asyncio # task that does work async def work(): # block for a moment await asyncio.sleep(1) # main coroutine async def main(): # create coroutine, but never run work() # block for a moment await asyncio.sleep(2) # report a message print('Done') # start the event loop asyncio.run(main()) |
Running the example first starts the asyncio event loop and runs the main() coroutine.
The main() coroutine runs and creates the work() coroutine but does not run it.
The program then emits two RuntimeWarning warnings internally, but the filtering we configured ensures that these warnings are suppressed.
Finally, the main() coroutine sleeps for two seconds and reports a done message.
This example highlights how we can suppress warnings in an asyncio program, preventing the warning messages from being reported.
1 |
Done |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of Logging Asyncio Warnings
We can explore an example of logging warnings in asyncio.
In this case, we can update the above example so that all warnings are emitted again.
1 2 3 |
... # emit all warnings warnings.filterwarnings('always') |
We can then configure the logging infrastructure so that log messages with a DEBUG-level and above are reported and that the logging infrastructure should capture warning messages.
1 2 3 4 5 |
... # configure logging logging.basicConfig(level=logging.DEBUG) # turn warnings into WARNING level log message. logging.captureWarnings(True) |
And that’s it.
This will allow the asyncio module to log debug messages. I will also allow the warning messages raised by the asyncio event loop to be logged as WARNING-level messages.
Tying this together, the complete example is listed below.
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 |
# SuperFastPython.com # example of capturing warning messages using logging import warnings # emit all warnings warnings.filterwarnings('always') import logging import asyncio # task that does work async def work(): # block for a moment await asyncio.sleep(1) # main coroutine async def main(): # create coroutine, but never run work() # block for a moment await asyncio.sleep(2) # report a message print('Done') # configure logging logging.basicConfig(level=logging.DEBUG) # turn warnings into WARNING level log message. logging.captureWarnings(True) # start the event loop asyncio.run(main()) |
Running the example first configures the logging infrastructure to log DEBUG messages and above, and to convert all emitted warnings into WARNING log messages.
It then starts the asyncio event loop and runs the main() coroutine.
The event loop logs a debug message, indicating the type of selector used within the event loop.
The main() coroutine runs and creates the work() coroutine but does not run it.
The program then emits one RuntimeWarning warning that highlights that the created work() coroutine was never awaited. This RuntimeWarning is reported
Finally, the main() coroutine sleeps for two seconds and reports a done message.
This example highlights how we can convert emitted warnings into WARNING level log messages so that they are not lost.
1 2 3 4 |
DEBUG:asyncio:Using selector: KqueueSelector WARNING:py.warnings:...: RuntimeWarning: coroutine 'work' was never awaited work() Done |
Further Reading
This section provides additional resources that you may find helpful.
Python Asyncio Books
- Python Asyncio Mastery, Jason Brownlee (my book!)
- Python Asyncio Jump-Start, Jason Brownlee.
- Python Asyncio Interview Questions, Jason Brownlee.
- Asyncio Module API Cheat Sheet
I also recommend the following books:
- Python Concurrency with asyncio, Matthew Fowler, 2022.
- Using Asyncio in Python, Caleb Hattingh, 2020.
- asyncio Recipes, Mohamed Mustapha Tahrioui, 2019.
Guides
APIs
- asyncio — Asynchronous I/O
- Asyncio Coroutines and Tasks
- Asyncio Streams
- Asyncio Subprocesses
- Asyncio Queues
- Asyncio Synchronization Primitives
References
Takeaways
You now know how to manage warnings in asyncio programs.
Did I make a mistake? See a typo?
I’m a simple humble human. Correct me, please!
Do you have any additional tips?
I’d love to hear about them!
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Eddy Billard on Unsplash
Do you have any questions?