|
6 | 6 | import itertools
|
7 | 7 | import tokenize
|
8 | 8 | from functools import reduce
|
9 |
| -from typing import Dict, List, NamedTuple, Optional, Tuple, Union, cast |
| 9 | +from typing import Dict, Iterator, List, NamedTuple, Optional, Tuple, Union, cast |
10 | 10 |
|
11 | 11 | import astroid
|
12 | 12 | from astroid.util import Uninferable
|
@@ -154,15 +154,24 @@ class ConsiderUsingWithStack(NamedTuple):
|
154 | 154 | class_scope: Dict[str, astroid.NodeNG] = {}
|
155 | 155 | function_scope: Dict[str, astroid.NodeNG] = {}
|
156 | 156 |
|
| 157 | + def __iter__(self) -> Iterator[Dict[str, astroid.NodeNG]]: |
| 158 | + yield from (self.function_scope, self.class_scope, self.module_scope) |
| 159 | + |
157 | 160 | def get_stack_for_frame(
|
158 | 161 | self, frame: Union[astroid.FunctionDef, astroid.ClassDef, astroid.Module]
|
159 | 162 | ):
|
| 163 | + """Get the stack corresponding to the scope of the given frame.""" |
160 | 164 | if isinstance(frame, astroid.FunctionDef):
|
161 | 165 | return self.function_scope
|
162 | 166 | if isinstance(frame, astroid.ClassDef):
|
163 | 167 | return self.class_scope
|
164 | 168 | return self.module_scope
|
165 | 169 |
|
| 170 | + def clear_all(self) -> None: |
| 171 | + """Convenience method to clear all stacks""" |
| 172 | + for stack in self: |
| 173 | + stack.clear() |
| 174 | + |
166 | 175 |
|
167 | 176 | class RefactoringChecker(checkers.BaseTokenChecker):
|
168 | 177 | """Looks for code which can be refactored
|
@@ -443,9 +452,7 @@ def _init(self):
|
443 | 452 | self._nested_blocks_msg = None
|
444 | 453 | self._reported_swap_nodes = set()
|
445 | 454 | self._can_simplify_bool_op = False
|
446 |
| - self._consider_using_with_stack.module_scope.clear() |
447 |
| - self._consider_using_with_stack.class_scope.clear() |
448 |
| - self._consider_using_with_stack.function_scope.clear() |
| 455 | + self._consider_using_with_stack.clear_all() |
449 | 456 |
|
450 | 457 | def open(self):
|
451 | 458 | # do this in open since config not fully initialized in __init__
|
@@ -621,11 +628,7 @@ def visit_excepthandler(self, node):
|
621 | 628 | def visit_with(self, node):
|
622 | 629 | for var, names in node.items:
|
623 | 630 | if isinstance(var, astroid.Name):
|
624 |
| - for stack in ( |
625 |
| - self._consider_using_with_stack.function_scope, |
626 |
| - self._consider_using_with_stack.class_scope, |
627 |
| - self._consider_using_with_stack.module_scope, |
628 |
| - ): |
| 631 | + for stack in self._consider_using_with_stack: |
629 | 632 | # We don't need to restrict the stacks we search to the current scope and outer scopes,
|
630 | 633 | # as e.g. the function_scope stack will be empty when we check a ``with`` on the class level.
|
631 | 634 | if var.name in stack:
|
@@ -1401,28 +1404,24 @@ def _append_context_managers_to_stack(self, node: astroid.Assign) -> None:
|
1401 | 1404 | if Uninferable in (assignees, values):
|
1402 | 1405 | return
|
1403 | 1406 | for assignee, value in zip(assignees, values):
|
1404 |
| - if isinstance(value, astroid.Call): |
1405 |
| - inferred = utils.safe_infer(value.func) |
1406 |
| - if ( |
1407 |
| - not inferred |
1408 |
| - or inferred.qname() not in CALLS_RETURNING_CONTEXT_MANAGERS |
1409 |
| - ): |
1410 |
| - continue |
1411 |
| - stack = self._consider_using_with_stack.get_stack_for_frame( |
1412 |
| - node.frame() |
1413 |
| - ) |
1414 |
| - varname = ( |
1415 |
| - assignee.name |
1416 |
| - if isinstance(assignee, astroid.AssignName) |
1417 |
| - else assignee.attrname |
| 1407 | + if not isinstance(value, astroid.Call): |
| 1408 | + continue |
| 1409 | + inferred = utils.safe_infer(value.func) |
| 1410 | + if not inferred or inferred.qname() not in CALLS_RETURNING_CONTEXT_MANAGERS: |
| 1411 | + continue |
| 1412 | + stack = self._consider_using_with_stack.get_stack_for_frame(node.frame()) |
| 1413 | + varname = ( |
| 1414 | + assignee.name |
| 1415 | + if isinstance(assignee, astroid.AssignName) |
| 1416 | + else assignee.attrname |
| 1417 | + ) |
| 1418 | + if varname in stack: |
| 1419 | + # variable was redefined before it was used in a ``with`` block |
| 1420 | + self.add_message( |
| 1421 | + "consider-using-with", |
| 1422 | + node=stack[varname], |
1418 | 1423 | )
|
1419 |
| - if varname in stack: |
1420 |
| - # variable was redefined before it was used in a ``with`` block |
1421 |
| - self.add_message( |
1422 |
| - "consider-using-with", |
1423 |
| - node=stack[varname], |
1424 |
| - ) |
1425 |
| - stack[varname] = value |
| 1424 | + stack[varname] = value |
1426 | 1425 |
|
1427 | 1426 | def _check_consider_using_with(self, node: astroid.Call):
|
1428 | 1427 | if _is_inside_context_manager(node):
|
|
0 commit comments