rspec/rules/S5650/python/rule.adoc

135 lines
3.3 KiB
Plaintext
Raw Normal View History

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.
== 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:
[source,text]
----
2023-07-31 10:18:28 +02:00
Entering
Executing body
Exiting
----
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
=== 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
----
=== 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
----
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]
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[]