This rule raises an issue when an object used as a context manager in a with or async with statement does not implement the corresponding context manager protocol.
The with statement is used to wrap the execution of a block of code with methods defined by a context manager. When entering a
with block, Python calls the object’s __enter__ method, and when leaving the block, it calls the object’s
__exit__ method. An object can therefore be used in a with statement only if its type implements both of
these methods. Because Python looks these methods up on the object’s type and not on the instance, they must be defined on the class itself or
inherited from a base class; assigning them as instance attributes does not satisfy the protocol.
The asynchronous variant async with relies on the asynchronous context manager protocol instead: the object’s type must implement
both __aenter__ and __aexit__.
Using an object that does not implement the required protocol raises a TypeError at runtime and interrupts the program:
__enter__ or __exit__ in a with statement__aenter__ or __aexit__ in an async with statementA common mistake is to implement only half of the protocol, for example __enter__ without __exit__, or to mix the
synchronous and asynchronous variants.
Make sure the object used in the statement implements the protocol matching the statement:
with, implement both __enter__ and __exit__, or use an existing context manager.async with, implement both __aenter__ and __aexit__, or use an existing asynchronous context
manager.The contextlib module also provides helpers, such as the @contextmanager and @asynccontextmanager
decorators, to build context managers from generator functions.
class Resource:
def __enter__(self):
return self
with Resource() as r: # Noncompliant: "Resource" does not implement "__exit__"
print(r)
class Resource:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
return False
with Resource() as r:
print(r)
class Connection:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
return False
async def handle():
async with Connection() as c: # Noncompliant: "Connection" does not implement the asynchronous context manager protocol
print(c)
class Connection:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_value, traceback):
return False
async def handle():
async with Connection() as c:
print(c)
with
statementcontextlib.contextmanager