2023-07-31 10:18:28 +02:00
This rule raises an issue when the object of a ``++with++`` statement is not a valid a context manager.
2023-05-03 11:06:20 +02:00
== Why is this an issue?
2023-07-31 10:18:28 +02:00
The https://docs.python.org/3/reference/compound_stmts.html#the-with-statement[``++with++`` statement] is used to wrap the execution of a block with methods defined by a context manager. The https://docs.python.org/3/reference/datamodel.html#context-managers[context manager] handles the entry into, and the exit from, the desired runtime context for the execution of the block of code. To do so, a context manager should have an ``++__enter__++`` and an ``++__exit__++`` method.
Executing the following block of code:
[source,python]
----
class MyContextManager:
def __enter__(self):
print("Entering")
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting")
with MyContextManager():
print("Executing body")
----
will output:
2023-10-30 10:33:56 +01:00
[source,text]
----
2023-07-31 10:18:28 +02:00
Entering
Executing body
Exiting
2023-10-30 10:33:56 +01:00
----
2023-07-31 10:18:28 +02:00
If either the ``++__enter__++`` or the ``++__exit__++`` method is missing, an ``AttributeError`` will be raised instead.
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
=== Note
When working with https://docs.python.org/3/reference/datamodel.html#async-context-managers[asynchronous context managers], the ``with`` statement should be replaced by ``++async with++``.
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
== How to fix it
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
To fix this issue, make sure that the object of the `with` statement is a valid context manager (implementing both ``++__enter__++`` and ``++__exit__++`` methods).
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
If the object of the `with` statement is an asynchronous context manager, make sure to use an `async with` statement.
== Code examples
2021-04-28 18:08:03 +02:00
2023-05-03 11:06:20 +02:00
=== Noncompliant code example
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
[source,python,diff-id=1,diff-type=noncompliant]
2021-04-28 16:49:39 +02:00
----
2023-07-31 10:18:28 +02:00
class MyContextManager:
...
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
with MyContextManager(): # Noncompliant: not a valid contex manager
...
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
def simple_contextmanager():
...
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
with simple_contextmanager() as e: # Noncompliant: not a valid contex manager
...
2021-04-28 16:49:39 +02:00
@asynccontextmanager
async def async_context_manager():
yield 42
2023-07-31 10:18:28 +02:00
def call_async_context_manager():
with async_context_manager() as context: # Noncompliant: missing "async"
...
2021-04-28 16:49:39 +02:00
----
2021-04-28 18:08:03 +02:00
2023-05-03 11:06:20 +02:00
=== Compliant solution
2021-04-28 16:49:39 +02:00
2023-07-31 10:18:28 +02:00
[source,python,diff-id=1,diff-type=compliant]
2021-04-28 16:49:39 +02:00
----
2023-07-31 10:18:28 +02:00
class MyContextManager:
def __enter__(self):
print("Entering")
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting")
with MyContextManager():
...
2021-04-28 16:49:39 +02:00
from contextlib import contextmanager, asynccontextmanager
@contextmanager
def simple_contextmanager():
print("enter")
yield
print("exit")
with simple_contextmanager() as e:
2023-07-31 10:18:28 +02:00
...
2021-04-28 16:49:39 +02:00
@asynccontextmanager
async def async_context_manager():
yield 42
async def call_async_context_manager():
async with async_context_manager() as context:
2023-07-31 10:18:28 +02:00
...
2021-04-28 16:49:39 +02:00
----
2021-04-28 18:08:03 +02:00
2023-07-31 10:18:28 +02:00
== Resources
=== Documentation
* Python Documentation - https://docs.python.org/3/reference/compound_stmts.html#the-with-statement[``++with++`` statement]
* Python Documentation - https://docs.python.org/3/reference/datamodel.html#context-managers[context managers]
2021-09-20 15:38:42 +02:00
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
2023-05-25 14:18:12 +02:00
=== Message
* Replace this expression with a context manager.
* Add "async" before "with"; Expression is an async context manager.
=== Highlighting
Primary: the expression used as a context manager
Secondary: the "with" keyword
2021-09-20 15:38:42 +02:00
endif::env-github,rspecator-view[]