summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Walls <jacobtylerwalls@gmail.com>2021-12-17 04:12:59 -0500
committerGitHub <noreply@github.com>2021-12-17 10:12:59 +0100
commit8743d895cf39b8e0eb53b4203de1e569ed1f2518 (patch)
tree3efbeaafc0a3fa14b3e0961184591ed7d5556263
parentc285fae493a7a272639a969d6ea914db8c9e1d18 (diff)
downloadpylint-git-8743d895cf39b8e0eb53b4203de1e569ed1f2518.tar.gz
Factor out `_uncertain_nodes_in_except_blocks()` (#5541)
-rw-r--r--pylint/checkers/variables.py85
1 files changed, 51 insertions, 34 deletions
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index a1cee36a0..27ad863f8 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -649,40 +649,12 @@ scope_type : {self._atomic.scope_type}
# Filter out assignments in an Except clause that the node is not
# contained in, assuming they may fail
if found_nodes:
- filtered_nodes = [
- n
- for n in found_nodes
- if not (
- isinstance(n.statement(future=True).parent, nodes.ExceptHandler)
- and isinstance(
- n.statement(future=True).parent.parent, nodes.TryExcept
- )
- # If the try block returns we assume that assignments in the except
- # handlers could have happened.
- and (
- not any(
- isinstance(try_statement, nodes.Return)
- for try_statement in n.statement(
- future=True
- ).parent.parent.body
- )
- # But not if this node is in the final block, which will
- # execute before the return.
- or (
- isinstance(node_statement.parent, nodes.TryFinally)
- and node_statement in node_statement.parent.finalbody
- and n.statement(future=True).parent.parent.parent.parent_of(
- node_statement
- )
- )
- )
- )
- or n.statement(future=True).parent.parent_of(node)
- ]
- filtered_nodes_set = set(filtered_nodes)
- difference = [n for n in found_nodes if n not in filtered_nodes_set]
- self.consumed_uncertain[node.name] += difference
- found_nodes = filtered_nodes
+ uncertain_nodes = self._uncertain_nodes_in_except_blocks(
+ found_nodes, node, node_statement
+ )
+ self.consumed_uncertain[node.name] += uncertain_nodes
+ uncertain_nodes_set = set(uncertain_nodes)
+ found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]
# If this node is in a Finally block of a Try/Finally,
# filter out assignments in the try portion, assuming they may fail
@@ -724,6 +696,51 @@ scope_type : {self._atomic.scope_type}
return found_nodes
+ @staticmethod
+ def _uncertain_nodes_in_except_blocks(found_nodes, node, node_statement):
+ """
+ Return any nodes in ``found_nodes`` that should be treated as uncertain
+ because they are in an except block.
+ """
+ uncertain_nodes = []
+ for other_node in found_nodes:
+ other_node_statement = other_node.statement(future=True)
+ # Only testing for statements in the except block of TryExcept
+ if not (
+ isinstance(other_node_statement.parent, nodes.ExceptHandler)
+ and isinstance(other_node_statement.parent.parent, nodes.TryExcept)
+ ):
+ continue
+ # If the other node is in the same scope as this node, assume it executes
+ if other_node_statement.parent.parent_of(node):
+ continue
+ try_block_returns = any(
+ isinstance(try_statement, nodes.Return)
+ for try_statement in other_node_statement.parent.parent.body
+ )
+ # If the try block returns, assume the except blocks execute.
+ if try_block_returns:
+ # Exception: if this node is in the final block of the other_node_statement,
+ # it will execute before returning. Assume the except statements are uncertain.
+ if (
+ isinstance(node_statement.parent, nodes.TryFinally)
+ and node_statement in node_statement.parent.finalbody
+ # We have already tested that other_node_statement has two parents
+ # and it was TryExcept, so getting one more parent is safe.
+ and other_node_statement.parent.parent.parent.parent_of(
+ node_statement
+ )
+ ):
+ uncertain_nodes.append(other_node)
+ else:
+ # Assume the except blocks execute. Possiblility for a false negative
+ # if one of the except blocks does not define the name in question,
+ # raise, or return. See: https://github.com/PyCQA/pylint/issues/5524.
+ continue
+ # Passed all tests for uncertain execution
+ uncertain_nodes.append(other_node)
+ return uncertain_nodes
+
# pylint: disable=too-many-public-methods
class VariablesChecker(BaseChecker):