diff --git a/ChangeLog b/ChangeLog index c3f4dbfb27..8fbe87334a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,10 @@ Release date: TBA .. Put bug fixes that should not wait for a new minor version here +* Fix crash if a callable returning a context manager was assigned to a list or dict item + + Closes #4732 + What's New in Pylint 2.9.4? =========================== diff --git a/doc/whatsnew/2.9.rst b/doc/whatsnew/2.9.rst index 669b9c8bc7..0c3b41577c 100644 --- a/doc/whatsnew/2.9.rst +++ b/doc/whatsnew/2.9.rst @@ -102,3 +102,5 @@ Other Changes * No longer emit ``consider-using-with`` for ``ThreadPoolExecutor`` and ``ProcessPoolExecutor`` as they have legitimate use cases without a ``with`` block. + +* Fix crash if a callable returning a context manager was assigned to a list or dict item diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py index 8bba92238a..3512451aca 100644 --- a/pylint/checkers/refactoring/refactoring_checker.py +++ b/pylint/checkers/refactoring/refactoring_checker.py @@ -1407,7 +1407,11 @@ def _append_context_managers_to_stack(self, node: astroid.Assign) -> None: if not isinstance(value, astroid.Call): continue inferred = utils.safe_infer(value.func) - if not inferred or inferred.qname() not in CALLS_RETURNING_CONTEXT_MANAGERS: + if ( + not inferred + or inferred.qname() not in CALLS_RETURNING_CONTEXT_MANAGERS + or not isinstance(assignee, (astroid.AssignName, astroid.AssignAttr)) + ): continue stack = self._consider_using_with_stack.get_stack_for_frame(node.frame()) varname = ( diff --git a/tests/functional/c/consider/consider_using_with.py b/tests/functional/c/consider/consider_using_with.py index 126bf2880d..fc2804455b 100644 --- a/tests/functional/c/consider/consider_using_with.py +++ b/tests/functional/c/consider/consider_using_with.py @@ -215,3 +215,17 @@ def my_nested_function(): ) with used_pool: pass + + +def test_subscript_assignment(): + """ + Regression test for issue https://github.com/PyCQA/pylint/issues/4732. + If a context manager is assigned to a list or dict, we are not able to + tell if / how the context manager is used later on, as it is not assigned + to a variable or attribute directly. + In this case we can only emit the message directly. + """ + job_list = [None, None] + job_list[0] = subprocess.Popen("ls") # [consider-using-with] + job_dict = {} + job_dict["myjob"] = subprocess.Popen("ls") # [consider-using-with] diff --git a/tests/functional/c/consider/consider_using_with.txt b/tests/functional/c/consider/consider_using_with.txt index 81de4266c4..45311b15b7 100644 --- a/tests/functional/c/consider/consider_using_with.txt +++ b/tests/functional/c/consider/consider_using_with.txt @@ -22,3 +22,5 @@ consider-using-with:201:4::Consider using 'with' for resource-allocating operati consider-using-with:202:4::Consider using 'with' for resource-allocating operations:HIGH consider-using-with:207:4::Consider using 'with' for resource-allocating operations:HIGH consider-using-with:213:4::Consider using 'with' for resource-allocating operations:HIGH +consider-using-with:229:18:test_subscript_assignment:Consider using 'with' for resource-allocating operations:HIGH +consider-using-with:231:24:test_subscript_assignment:Consider using 'with' for resource-allocating operations:HIGH