Skip to content

Commit d16730b

Browse files
Fix #4045: Don't assume try ancestors are immediate parents when emitting used-before-assignment
1 parent 2d949d9 commit d16730b

File tree

5 files changed

+66
-6
lines changed

5 files changed

+66
-6
lines changed

ChangeLog

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Release date: TBA
1616

1717
Closes #85, #2615
1818

19+
* Fixed false negative for ``used-before-assignment`` when a conditional
20+
or context manager intervened before the try statement that suggested
21+
it might fail.
22+
23+
Closes #4045
24+
1925
* Fixed extremely long processing of long lines with comma's.
2026

2127
Closes #5483

doc/whatsnew/2.13.rst

+6
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ Other Changes
6363

6464
Closes #85, #2615
6565

66+
* Fixed false negative for ``used-before-assignment`` when a conditional
67+
or context manager intervened before the try statement that suggested
68+
it might fail.
69+
70+
Closes #4045
71+
6672
* Fix a false positive for ``assigning-non-slot`` when the slotted class
6773
defined ``__setattr__``.
6874

pylint/checkers/variables.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ def get_next_to_consume(self, node):
677677

678678
# If this node is in an ExceptHandler,
679679
# filter out assignments in the try portion, assuming they may fail
680-
if found_nodes and isinstance(node_statement.parent, nodes.ExceptHandler):
680+
if found_nodes:
681681
uncertain_nodes = (
682682
self._uncertain_nodes_in_try_blocks_when_evaluating_except_blocks(
683683
found_nodes, node_statement
@@ -738,17 +738,28 @@ def _uncertain_nodes_in_except_blocks(found_nodes, node, node_statement):
738738
def _uncertain_nodes_in_try_blocks_when_evaluating_except_blocks(
739739
found_nodes, node_statement
740740
):
741+
closest_except_handler = utils.get_node_first_ancestor_of_type(
742+
node_statement, nodes.ExceptHandler
743+
)
744+
if closest_except_handler is None:
745+
return []
746+
closest_try_ancestor = utils.get_node_first_ancestor_of_type(
747+
node_statement, nodes.TryExcept
748+
)
741749
uncertain_nodes = []
742750
for other_node in found_nodes:
743751
other_node_statement = other_node.statement(future=True)
752+
if other_node_statement is closest_except_handler:
753+
continue
744754
other_node_try_ancestor = utils.get_node_first_ancestor_of_type(
745755
other_node_statement, nodes.TryExcept
746756
)
747-
if other_node_try_ancestor is None:
748-
continue
749-
if other_node_statement not in other_node_try_ancestor.body:
757+
if other_node_try_ancestor is not closest_try_ancestor:
750758
continue
751-
if node_statement.parent not in other_node_try_ancestor.handlers:
759+
other_node_except_handler = utils.get_node_first_ancestor_of_type(
760+
other_node_statement, nodes.ExceptHandler
761+
)
762+
if other_node_except_handler is closest_except_handler:
752763
continue
753764
# Passed all tests for uncertain execution
754765
uncertain_nodes.append(other_node)

tests/functional/u/use/used_before_assignment_issue2615.py

+36
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,42 @@ def main():
44
try:
55
res = 1 / 0
66
res = 42
7+
if main():
8+
res = None
9+
with open(__file__, encoding="utf-8") as opened_file:
10+
res = opened_file.readlines()
711
except ZeroDivisionError:
812
print(res) # [used-before-assignment]
913
print(res)
14+
15+
16+
def nested_except_blocks():
17+
"""Don't confuse except blocks with each other."""
18+
try:
19+
res = 1 / 0
20+
res = 42
21+
if main():
22+
res = None
23+
with open(__file__, encoding="utf-8") as opened_file:
24+
res = opened_file.readlines()
25+
except ZeroDivisionError:
26+
try:
27+
more_bad_division = 1 / 0
28+
except ZeroDivisionError:
29+
print(more_bad_division) # [used-before-assignment]
30+
print(res) # don't emit: too nested to make confident inferences
31+
print(res)
32+
33+
34+
def name_earlier_in_except_block():
35+
"""Permit the name that might not have been assigned during the try block
36+
to be defined inside a conditional inside the except block.
37+
"""
38+
try:
39+
res = 1 / 0
40+
except ZeroDivisionError:
41+
if main():
42+
res = 10
43+
else:
44+
res = 11
45+
print(res)
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
used-before-assignment:8:14:8:17:main:Using variable 'res' before assignment:UNDEFINED
1+
used-before-assignment:12:14:12:17:main:Using variable 'res' before assignment:UNDEFINED
2+
used-before-assignment:29:18:29:35:nested_except_blocks:Using variable 'more_bad_division' before assignment:UNDEFINED

0 commit comments

Comments
 (0)