Asyncio Coroutine Function and Coroutine Types

January 15, 2023 Python Asyncio

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.

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.

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

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.

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

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.

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

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.

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

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.

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

__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

Takeaways

You now know about coroutine functions and coroutine types in Python.



If you enjoyed this tutorial, you will love my book: Python Asyncio Jump-Start. It covers everything you need to master the topic with hands-on examples and clear explanations.