rspec/rules/S5650/python/rule.adoc
Fred Tingaud d3cfe19d7e
Fix broken or dangerous backquotes
Co-authored-by: Marco Borgeaud <89914223+marco-antognini-sonarsource@users.noreply.github.com>
2023-10-30 10:33:56 +01:00

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[]