This rule raises an issue when a function is declared async but does not use any asynchronous features.

Why is this an issue?

Declaring a function with async def signals that it’s a coroutine, typically because it needs to use await for non-blocking operations (like I/O), or employs other asynchronous features like async for or async with.

If none of these asynchronous mechanisms are utilized within the function’s body, the async declaration is superfluous.

Using async unnecessarily can:

How to fix it

Either remove the async keyword, or start using the appropriate asynchronous features.

An async method may also need to remain awaitable to fulfil an inherited contract. In that case, make the contract explicit in the code, for example by inheriting from the relevant base class or protocol and marking the method as an override.

Code examples

Noncompliant code example

async def my_function():  # Noncompliant
    print("Hello from my function")

Compliant solution

def my_function():  # Compliant
    print("Hello from my function")

Noncompliant code example

def another_function(): ...

async def my_function():  # Noncompliant
    return another_function()

Compliant solution

async def another_function(): ...

async def my_function():  # Compliant
    return await another_function()

Exceptions

This rule does not raise an issue in the following cases:

import httpx

async def log_request(request: httpx.Request) -> None:  # Compliant - required coroutine callback
    print(f"Request: {request.method} {request.url}")

client = httpx.AsyncClient(event_hooks={"request": [log_request]})
from confluent_kafka.aio import AIOConsumer

async def on_assign(consumer, partitions) -> None:  # Compliant - required coroutine callback
    print(f"Assigned: {partitions}")

consumer = AIOConsumer({"bootstrap.servers": "localhost:9092", "group.id": "mygroup"})
await consumer.subscribe(["topic"], on_assign=on_assign)

Resources

Articles & blog posts