
Co-authored-by: Marco Borgeaud <89914223+marco-antognini-sonarsource@users.noreply.github.com>
135 lines
3.3 KiB
Plaintext
135 lines
3.3 KiB
Plaintext
This rule raises an issue when the object of a ``++with++`` statement is not a valid a context manager.
|
|
|
|
== Why is this an issue?
|
|
|
|
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:
|
|
[source,text]
|
|
----
|
|
Entering
|
|
Executing body
|
|
Exiting
|
|
----
|
|
|
|
If either the ``++__enter__++`` or the ``++__exit__++`` method is missing, an ``AttributeError`` will be raised instead.
|
|
|
|
|
|
=== 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++``.
|
|
|
|
== How to fix it
|
|
|
|
To fix this issue, make sure that the object of the `with` statement is a valid context manager (implementing both ``++__enter__++`` and ``++__exit__++`` methods).
|
|
|
|
If the object of the `with` statement is an asynchronous context manager, make sure to use an `async with` statement.
|
|
|
|
== Code examples
|
|
|
|
=== Noncompliant code example
|
|
|
|
[source,python,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
class MyContextManager:
|
|
...
|
|
|
|
with MyContextManager(): # Noncompliant: not a valid contex manager
|
|
...
|
|
|
|
def simple_contextmanager():
|
|
...
|
|
|
|
with simple_contextmanager() as e: # Noncompliant: not a valid contex manager
|
|
...
|
|
|
|
@asynccontextmanager
|
|
async def async_context_manager():
|
|
yield 42
|
|
|
|
def call_async_context_manager():
|
|
with async_context_manager() as context: # Noncompliant: missing "async"
|
|
...
|
|
|
|
----
|
|
|
|
|
|
=== Compliant solution
|
|
|
|
[source,python,diff-id=1,diff-type=compliant]
|
|
----
|
|
class MyContextManager:
|
|
def __enter__(self):
|
|
print("Entering")
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
print("Exiting")
|
|
|
|
|
|
with MyContextManager():
|
|
...
|
|
|
|
from contextlib import contextmanager, asynccontextmanager
|
|
|
|
@contextmanager
|
|
def simple_contextmanager():
|
|
print("enter")
|
|
yield
|
|
print("exit")
|
|
|
|
with simple_contextmanager() as e:
|
|
...
|
|
|
|
@asynccontextmanager
|
|
async def async_context_manager():
|
|
yield 42
|
|
|
|
async def call_async_context_manager():
|
|
async with async_context_manager() as context:
|
|
...
|
|
----
|
|
|
|
== 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]
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== 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
|
|
|
|
|
|
endif::env-github,rspecator-view[]
|