You can programmatically identify coroutine functions and coroutines using the inspect module API.
Coroutines have a specific “coroutine” type that shares methods with generators as well as the awaitable interface.
In this tutorial, you will discover coroutine functions and coroutine types in Python.
Let’s get started.
Asyncio Coroutine Types
Modern Python includes changes to the language to support coroutines for asynchronous programming.
This includes a coroutine function type and a coroutine type for creating coroutines.
For example, the typing.Coroutine class.
As such, Python provides tools for inspecting the coroutine type.
For example, the inspect.iscoroutinefunction(), inspect.iscoroutine(), and inspect.isawaitable() functions.
These functions can be helpful to better understand coroutine functions and coroutines themselves in asynchronous programming.
Let’s take a closer look at the coroutine function and coroutine types and how to identify them.
Run loops using all CPUs, download your FREE book to learn how.
What is the Coroutine Function Type
A coroutine function is a function defined using the “async def” expression.
It is a function that when called will create and return a coroutine.
You can learn more about the async def expression in the tutorial:
How to Check The Type of a Coroutine Function
The first check we might perform on a coroutine function is to report its type.
The example below defines a coroutine function, then reports its type directly using the type() built-in function.
1 2 3 4 5 6 7 8 9 |
# SuperFastPython.com # Check the type of a coroutine function # define a coroutine function async def custom_coro(): pass # check the type of the coroutine function print(type(custom_coro)) |
Running the example defines the coroutine function and then reports its type.
In this case, we can see that a coroutine function has the type of function, the same as a regular function.
This highlights that the type directly does not provide sufficient information to identify a coroutine function.
1 |
class 'function' |
Next, let’s look at introspection.
How to Identify a Coroutine Function
Python provides the “inspect” module that contains many tools for introspection.
This includes the inspect.iscoroutinefunction() function that will return true if the provided Python object is a coroutine function, or false otherwise.
Return True if the object is a coroutine function (a function defined with an async def syntax).
— inspect — Inspect live objects
The example below defines both a coroutine function and a regular Python function, then checks each if they are coroutine functions using the introspection function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# SuperFastPython.com # check the type of a coroutine function using introspection import inspect # define a coroutine function async def custom_coro(): pass # define a function def custom_function(): pass # check that the coroutine function is identified print(inspect.iscoroutinefunction(custom_coro)) # check that the function is identified not identified print(inspect.iscoroutinefunction(custom_function)) |
Running the example first checks if our custom coroutine function can be identified as a coroutine function.
In this case, it can and the inspect.iscoroutinefunction() function returns True.
Next, the same procedure is tested on the regular Python function, which is correctly identified as not being a coroutine function.
This highlights that we can use the inspection.iscoroutinefunction() function to identify coroutine functions.
1 2 |
True False |
Next, let’s look at the coroutine type.
What is the Coroutine Type
Coroutines have a specific object type called “coroutine”.
In this section, we will explore the coroutine type and how to identify a coroutine object programmatically.
you can learn more about coroutines in the tutorial:
How to Check The Type of a Coroutine
We can report the type of a coroutine.
In the example below, we define a custom coroutine, then create a coroutine object by calling the coroutine function and report its type using the built-in type() function.
1 2 3 4 5 6 7 8 9 10 11 |
# SuperFastPython.com # Check the type of a coroutine # define a coroutine function async def custom_coro(): pass # create the coroutine object coro = custom_coro() # check the type of the coroutine print(type(coro)) |
Running the example first creates the coroutine, then reports its type.
We can see that indeed coroutine objects have the type “coroutine”.
The example also raises a RuntimeWarning as we never await (execute) the coroutine that we created.
1 2 |
class 'coroutine' sys:1: RuntimeWarning: coroutine 'custom_coro' was never awaited |
Next, let’s explore how we might identify a coroutine programmatically.
How to Identify a Coroutine
We can identify a coroutine object programmatically.
This can be achieved using the inspect.iscoroutine() function that will return true if the provided Python object is a coroutine, or false otherwise.
Return True if the object is a coroutine created by an async def function.
— inspect — Inspect live objects
A coroutine is awaitable. That is, it can be scheduled for execution in the asyncio runtime using the “await” expression.
Other objects are awaitable that are not coroutine, including asyncio.Future and asyncio.Task. An awaitable object implements the __await__() method.
The “inspect” module also provides the inspect.isawaitable() function to check if a Python object is awaitable.
Return True if the object can be used in await expression.
— inspect — Inspect live objects
The example below creates a coroutine and first confirms that it is a coroutine object and then that it is an awaitable object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# SuperFastPython.com # check the type of a coroutine using introspection import inspect # define a coroutine function async def custom_coro(): pass # create the coroutine object coro = custom_coro() # check that the coroutine is identified print(inspect.iscoroutine(coro)) # check that the coroutine is awaitable print(inspect.isawaitable(coro)) |
Running the example first creates the coroutine.
It then uses introspection to check if the coroutine object is indeed a coroutine type via the inspect.iscoroutine() function. As expected, the coroutine is correctly identified.
The example then checks if the coroutine is an awaitable via the inspect.isawaitable() function, which is also confirmed.
A warning is raised because we did not explicitly execute the coroutine that we created.
1 2 3 |
True True sys:1: RuntimeWarning: coroutine 'custom_coro' was never awaited |
Next, let’s explore the capabilities of a coroutine object.
What are the Methods on a Coroutine
A coroutine object has an API.
Although we never need to explicitly use it.
We can create a coroutine object and then report all of the methods on the object to get a sense of the available API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# SuperFastPython.com # check all the methods on a coroutine object # define a coroutine function async def custom_coro(): pass # create the coroutine object coro = custom_coro() # report all properties of the object for method in dir(coro): # check if callable if callable(getattr(coro, method)): print(method) |
The example below creates a coroutine and then reports all of the methods available.
We can see a number of coroutine-specific methods, such as the throw(), close(), and send() that come from the generator heritage.
Coroutines are based on generators internally, thus they share the implementation. Similarly to generator objects, coroutines have throw(), send() and close() methods.
— PEP 492 – Coroutines with async and await syntax
We can also see that the coroutine implements the __await__() method, required by awaitables.
Finally, a warning is raised again because we did not explicitly execute the coroutine that we created.
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 |
__await__ __class__ __del__ __delattr__ __dir__ __eq__ __format__ __ge__ __getattribute__ __gt__ __hash__ __init__ __init_subclass__ __le__ __lt__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __sizeof__ __str__ __subclasshook__ close send throw sys:1: RuntimeWarning: coroutine 'custom_coro' was never awaited |
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.
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
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Takeaways
You now know about coroutine functions and coroutine types in Python.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Rowan Heuvel on Unsplash
Do you have any questions?