This rule raises when using a regular with statement inside an async function with a context manager that implements the asynchronous context manager protocol.

Why is this an issue?

When working within an async function, it is important to maintain consistency with the asynchronous programming model. If a context manager implements the asynchronous context manager protocol (defining __aenter__ and __aexit__ methods), it should be used with the async with statement rather than the regular with statement.

The asynchronous context manager protocol is specifically designed to handle resources that may require asynchronous setup or teardown operations. Using the regular with statement in an async context bypasses this intended asynchronous behavior.

What is the potential impact?

Not following the proper async pattern can lead to:

How to fix it

Use the async with statement when working with asynchronous context managers.

Code examples

Noncompliant code example

class Resource:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc, tb):
        ...

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, tb):
        ...

async def main():
    resource = Resource()
    with resource:  # Noncompliant: using 'with' in async function when async protocol is available
        ...

Compliant solution

class Resource:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc, tb):
        ...

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, tb):
        ...

async def main():
    async with Resource() as resource:  # Compliant: using 'async with' in async function
        ...

How does this work?

The async with statement provides the proper way to use asynchronous context managers:

  1. It calls the __aenter__ method and awaits its result
  2. Assigns the returned value to the variable after as (if specified)
  3. Executes the code block within the context
  4. Calls the __aexit__ method and awaits its completion, even if an exception occurs

This ensures consistency with the async programming model and allows the context manager to perform any necessary asynchronous operations during setup and cleanup.

Resources

Documentation