Skip to content

Commit

Permalink
The last except handler wins when inferring variables bound in an exc…
Browse files Browse the repository at this point in the history
…ept handler.

Close pylint-dev/pylint#2777
  • Loading branch information
PCManticore committed Mar 8, 2019
1 parent 7a88072 commit f8155a0
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 2 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Release Date: 2019-03-02
Close PyCQA/pylint#2776


* The last except handler wins when inferring variables bound in an except handler.

Close PyCQA/pylint#2777

What's New in astroid 2.2.0?
============================
Release Date: 2019-02-27
Expand Down
16 changes: 14 additions & 2 deletions astroid/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,16 @@ def ilookup(self, name):
context = contextmod.InferenceContext()
return bases._infer_stmts(stmts, context, frame)

def _get_filtered_node_statements(self, nodes):
statements = {node: node.statement() for node in nodes}
# Next we check if we have ExceptHandlers that are parent
# of the underlying variable, in which case the last one survives
if all(isinstance(stmt, ExceptHandler) for stmt in statements.values()):
statements = {
node: stmt for node, stmt in statements.items() if stmt.parent_of(self)
}
return statements

def _filter_stmts(self, stmts, frame, offset):
"""Filter the given list of statements to remove ignorable statements.
Expand Down Expand Up @@ -1149,10 +1159,12 @@ def _filter_stmts(self, stmts, frame, offset):
else:
# disabling lineno filtering
mylineno = 0

_stmts = []
_stmt_parents = []
for node in stmts:
stmt = node.statement()
statements = self._get_filtered_node_statements(stmts)

for node, stmt in statements.items():
# line filtering is on and we have reached our location, break
if stmt.fromlineno > mylineno > 0:
break
Expand Down
59 changes: 59 additions & 0 deletions astroid/tests/unittest_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -5090,5 +5090,64 @@ def test(self):
assert isinstance(inferred, Slice)


def test_exception_lookup_last_except_handler_wins():
node = extract_node(
"""
try:
1/0
except ValueError as exc:
pass
try:
1/0
except OSError as exc:
exc #@
"""
)
inferred = node.inferred()
assert len(inferred) == 1
inferred_exc = inferred[0]
assert isinstance(inferred_exc, Instance)
assert inferred_exc.name == "OSError"

# Check that two except handlers on the same TryExcept works the same as separate
# TryExcepts
node = extract_node(
"""
try:
1/0
except ZeroDivisionError as exc:
pass
except ValueError as exc:
exc #@
"""
)
inferred = node.inferred()
assert len(inferred) == 1
inferred_exc = inferred[0]
assert isinstance(inferred_exc, Instance)
assert inferred_exc.name == "ValueError"


def test_exception_lookup_name_bound_in_except_handler():
node = extract_node(
"""
try:
1/0
except ValueError:
name = 1
try:
1/0
except OSError:
name = 2
name #@
"""
)
inferred = node.inferred()
assert len(inferred) == 1
inferred_exc = inferred[0]
assert isinstance(inferred_exc, nodes.Const)
assert inferred_exc.value == 2


if __name__ == "__main__":
unittest.main()

0 comments on commit f8155a0

Please sign in to comment.