Skip to content

Commit f9df028

Browse files
authored
Issue 4430 false positive consider-using-with R1732 (#4453)
* Suppress consider-using-with if used inside context manager * Added ChangeLog entry
1 parent 9528500 commit f9df028

File tree

6 files changed

+99
-31
lines changed

6 files changed

+99
-31
lines changed

ChangeLog

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Release date: 2021-04-26
1111
..
1212
Put new features and bugfixes here and also in 'doc/whatsnew/2.9.rst'
1313

14+
* Suppress ``consider-using-with`` inside context managers.
15+
16+
Closes #4430
17+
1418
* Added ``--fail-on`` option to return non-zero exit codes regardless of ``--fail-under`` value.
1519

1620
* numversion tuple contains integers again to fix multiple pylint's plugins that relied on it

pylint/checkers/refactoring/refactoring_checker.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ def get_curline_index_start():
103103
return False
104104

105105

106+
def _is_inside_context_manager(node):
107+
frame = node.frame()
108+
if not isinstance(
109+
frame, (astroid.FunctionDef, astroid.BoundMethod, astroid.UnboundMethod)
110+
):
111+
return False
112+
return frame.name == "__enter__" or utils.decorated_with(
113+
frame, "contextlib.contextmanager"
114+
)
115+
116+
106117
class RefactoringChecker(checkers.BaseTokenChecker):
107118
"""Looks for code which can be refactored
108119
@@ -1272,12 +1283,20 @@ def _check_consider_using_with_instead_assign(self, node: astroid.Assign):
12721283
assigned = node.value
12731284
if isinstance(assigned, astroid.Call):
12741285
inferred = utils.safe_infer(assigned.func)
1275-
if inferred and inferred.qname() in CALLS_RETURNING_CONTEXT_MANAGERS:
1286+
if (
1287+
inferred
1288+
and inferred.qname() in CALLS_RETURNING_CONTEXT_MANAGERS
1289+
and not _is_inside_context_manager(node)
1290+
):
12761291
self.add_message("consider-using-with", node=node)
12771292

12781293
def _check_consider_using_with_instead_call(self, node: astroid.Call):
12791294
inferred = utils.safe_infer(node.func)
1280-
if inferred and inferred.qname() in CALLS_THAT_COULD_BE_REPLACED_BY_WITH:
1295+
if (
1296+
inferred
1297+
and inferred.qname() in CALLS_THAT_COULD_BE_REPLACED_BY_WITH
1298+
and not _is_inside_context_manager(node)
1299+
):
12811300
self.add_message("consider-using-with", node=node)
12821301

12831302
def _check_consider_using_join(self, aug_assign):

tests/functional/c/consider/consider_using_with.py

+26-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# pylint: disable=missing-function-docstring, missing-module-docstring, invalid-name, import-outside-toplevel
22
import codecs
3+
import contextlib
34
import multiprocessing
45
import subprocess
56
import tarfile
@@ -81,14 +82,6 @@ def test_lock_acquisition():
8182
with rlock: # must not trigger
8283
pass
8384

84-
# Not working currently
85-
# condition = threading.Condition()
86-
# condition.acquire()
87-
# condition.release()
88-
89-
# with condition: # must not trigger
90-
# pass
91-
9285
sema = threading.Semaphore()
9386
sema.acquire() # [consider-using-with]
9487
sema.release()
@@ -104,6 +97,31 @@ def test_lock_acquisition():
10497
pass
10598

10699

100+
@contextlib.contextmanager
101+
def test_lock_acquisition_in_context_manager1():
102+
"""
103+
The message must not be triggered if the resource allocation is done inside a context manager.
104+
"""
105+
lock = threading.Lock()
106+
lock.acquire() # must not trigger
107+
yield
108+
lock.release()
109+
110+
111+
class MyLockContext:
112+
"""
113+
The message must not be triggered if the resource allocation is done inside a context manager.
114+
"""
115+
def __init__(self):
116+
self.lock = threading.Lock()
117+
118+
def __enter__(self):
119+
self.lock.acquire() # must not trigger
120+
121+
def __exit__(self, exc_type, exc_value, traceback):
122+
self.lock.release()
123+
124+
107125
def test_multiprocessing():
108126
# the different Locks provided by multiprocessing would be candidates
109127
# for consider-using-with as well, but they lead to InferenceErrors.
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
consider-using-with:14:4:test_codecs_open:Consider using 'with' for resource-allocating operations
2-
consider-using-with:19:4:test_urlopen:Consider using 'with' for resource-allocating operations
3-
consider-using-with:23:4:test_temporary_file:Consider using 'with' for resource-allocating operations
4-
consider-using-with:27:4:test_named_temporary_file:Consider using 'with' for resource-allocating operations
5-
consider-using-with:31:4:test_spooled_temporary_file:Consider using 'with' for resource-allocating operations
6-
consider-using-with:35:4:test_temporary_directory:Consider using 'with' for resource-allocating operations
7-
consider-using-with:39:4:test_zipfile:Consider using 'with' for resource-allocating operations
1+
consider-using-with:15:4:test_codecs_open:Consider using 'with' for resource-allocating operations
2+
consider-using-with:20:4:test_urlopen:Consider using 'with' for resource-allocating operations
3+
consider-using-with:24:4:test_temporary_file:Consider using 'with' for resource-allocating operations
4+
consider-using-with:28:4:test_named_temporary_file:Consider using 'with' for resource-allocating operations
5+
consider-using-with:32:4:test_spooled_temporary_file:Consider using 'with' for resource-allocating operations
6+
consider-using-with:36:4:test_temporary_directory:Consider using 'with' for resource-allocating operations
87
consider-using-with:40:4:test_zipfile:Consider using 'with' for resource-allocating operations
9-
consider-using-with:44:4:test_pyzipfile:Consider using 'with' for resource-allocating operations
10-
consider-using-with:49:4:test_pyzipfile:Consider using 'with' for resource-allocating operations
11-
consider-using-with:56:4:test_tarfile:Consider using 'with' for resource-allocating operations
12-
consider-using-with:62:4:test_tarfile:Consider using 'with' for resource-allocating operations
13-
consider-using-with:71:4:test_lock_acquisition:Consider using 'with' for resource-allocating operations
14-
consider-using-with:78:4:test_lock_acquisition:Consider using 'with' for resource-allocating operations
8+
consider-using-with:41:4:test_zipfile:Consider using 'with' for resource-allocating operations
9+
consider-using-with:45:4:test_pyzipfile:Consider using 'with' for resource-allocating operations
10+
consider-using-with:50:4:test_pyzipfile:Consider using 'with' for resource-allocating operations
11+
consider-using-with:57:4:test_tarfile:Consider using 'with' for resource-allocating operations
12+
consider-using-with:63:4:test_tarfile:Consider using 'with' for resource-allocating operations
13+
consider-using-with:72:4:test_lock_acquisition:Consider using 'with' for resource-allocating operations
14+
consider-using-with:79:4:test_lock_acquisition:Consider using 'with' for resource-allocating operations
15+
consider-using-with:86:4:test_lock_acquisition:Consider using 'with' for resource-allocating operations
1516
consider-using-with:93:4:test_lock_acquisition:Consider using 'with' for resource-allocating operations
16-
consider-using-with:100:4:test_lock_acquisition:Consider using 'with' for resource-allocating operations
17-
consider-using-with:110:4:test_multiprocessing:Consider using 'with' for resource-allocating operations
18-
consider-using-with:115:4:test_multiprocessing:Consider using 'with' for resource-allocating operations
19-
consider-using-with:120:4:test_multiprocessing:Consider using 'with' for resource-allocating operations
20-
consider-using-with:126:4:test_futures:Consider using 'with' for resource-allocating operations
21-
consider-using-with:130:4:test_futures:Consider using 'with' for resource-allocating operations
22-
consider-using-with:136:4:test_popen:Consider using 'with' for resource-allocating operations
17+
consider-using-with:128:4:test_multiprocessing:Consider using 'with' for resource-allocating operations
18+
consider-using-with:133:4:test_multiprocessing:Consider using 'with' for resource-allocating operations
19+
consider-using-with:138:4:test_multiprocessing:Consider using 'with' for resource-allocating operations
20+
consider-using-with:144:4:test_futures:Consider using 'with' for resource-allocating operations
21+
consider-using-with:148:4:test_futures:Consider using 'with' for resource-allocating operations
22+
consider-using-with:154:4:test_popen:Consider using 'with' for resource-allocating operations

tests/functional/c/consider/consider_using_with_open.py

+26
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
# pylint: disable=missing-function-docstring, missing-module-docstring, invalid-name, import-outside-toplevel
2+
# pylint: disable=missing-class-docstring, too-few-public-methods, unused-variable
23
"""
34
The functional test for the standard ``open()`` function has to be moved in a separate file,
45
because PyPy has to be excluded for the tests as the ``open()`` function is uninferable in PyPy.
56
However, all remaining checks for consider-using-with work in PyPy, so we do not want to exclude
67
PyPy from ALL functional tests.
78
"""
9+
from contextlib import contextmanager
10+
11+
12+
myfile = open("test.txt") # [consider-using-with]
813

914

1015
def test_open():
@@ -13,3 +18,24 @@ def test_open():
1318

1419
with open("test.txt") as fh: # must not trigger
1520
fh.read()
21+
22+
23+
def test_open_in_enter():
24+
"""Message must not trigger if the resource is allocated in a context manager."""
25+
class MyContextManager:
26+
def __init__(self):
27+
self.file_handle = None
28+
29+
def __enter__(self):
30+
self.file_handle = open("foo.txt", "w") # must not trigger
31+
32+
def __exit__(self, exc_type, exc_value, traceback):
33+
self.file_handle.close()
34+
35+
36+
@contextmanager
37+
def test_open_in_with_contextlib():
38+
"""Message must not trigger if the resource is allocated in a context manager."""
39+
file_handle = open("foo.txt", "w") # must not trigger
40+
yield file_handle
41+
file_handle.close()
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
consider-using-with:11:4:test_open:Consider using 'with' for resource-allocating operations
1+
consider-using-with:12:0::Consider using 'with' for resource-allocating operations
2+
consider-using-with:16:4:test_open:Consider using 'with' for resource-allocating operations

0 commit comments

Comments
 (0)