Skip to content

Commit 76dcd07

Browse files
Prevent used-before-assignment for names only defined under except if the else returns (#6791)
1 parent b3523ae commit 76dcd07

File tree

4 files changed

+83
-2
lines changed

4 files changed

+83
-2
lines changed

doc/whatsnew/2/2.14/full.rst

+5
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ Release date: 2022-06-01
9797

9898
Refs #6462
9999

100+
* Fixed a false positive regression in 2.13 for ``used-before-assignment`` where it is safe to rely
101+
on a name defined only in an ``except`` block because the ``else`` block returned.
102+
103+
Closes #6790
104+
100105
* Removed the ``assign-to-new-keyword`` message as there are no new keywords in the supported Python
101106
versions any longer.
102107

pylint/checkers/variables.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -680,12 +680,16 @@ def _uncertain_nodes_in_except_blocks(
680680
if closest_except_handler.parent_of(node):
681681
continue
682682
closest_try_except: nodes.TryExcept = closest_except_handler.parent
683+
# If the try or else blocks return, assume the except blocks execute.
683684
try_block_returns = any(
684685
isinstance(try_statement, nodes.Return)
685686
for try_statement in closest_try_except.body
686687
)
687-
# If the try block returns, assume the except blocks execute.
688-
if try_block_returns:
688+
else_block_returns = any(
689+
isinstance(else_statement, nodes.Return)
690+
for else_statement in closest_try_except.orelse
691+
)
692+
if try_block_returns or else_block_returns:
689693
# Exception: if this node is in the final block of the other_node_statement,
690694
# it will execute before returning. Assume the except statements are uncertain.
691695
if (
@@ -694,6 +698,13 @@ def _uncertain_nodes_in_except_blocks(
694698
and closest_try_except.parent.parent_of(node_statement)
695699
):
696700
uncertain_nodes.append(other_node)
701+
# Or the node_statement is in the else block of the relevant TryExcept
702+
elif (
703+
isinstance(node_statement.parent, nodes.TryExcept)
704+
and node_statement in node_statement.parent.orelse
705+
and closest_try_except.parent.parent_of(node_statement)
706+
):
707+
uncertain_nodes.append(other_node)
697708
# Assume the except blocks execute, so long as each handler
698709
# defines the name, raises, or returns.
699710
elif all(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""If the else block returns, it is generally safe to rely on assignments in the except."""
2+
3+
4+
def valid():
5+
"""https://github.com/PyCQA/pylint/issues/6790"""
6+
try:
7+
pass
8+
except ValueError:
9+
error = True
10+
else:
11+
return
12+
13+
print(error)
14+
15+
16+
def invalid():
17+
"""The finally will execute before the else returns."""
18+
try:
19+
pass
20+
except ValueError:
21+
error = None
22+
else:
23+
return
24+
finally:
25+
print(error) # [used-before-assignment]
26+
27+
28+
def invalid_2():
29+
"""The else does not return in every branch."""
30+
try:
31+
pass
32+
except ValueError:
33+
error = None
34+
else:
35+
if range(0):
36+
return
37+
finally:
38+
print(error) # [used-before-assignment]
39+
40+
41+
def invalid_3():
42+
"""Not every except defines the name."""
43+
try:
44+
pass
45+
except ValueError:
46+
error = None
47+
except KeyError:
48+
pass
49+
finally:
50+
print(error) # [used-before-assignment]
51+
52+
53+
def invalid_4():
54+
"""Should not rely on the name in the else even if it returns."""
55+
try:
56+
pass
57+
except ValueError:
58+
error = True
59+
else:
60+
print(error) # [used-before-assignment]
61+
return
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
used-before-assignment:25:14:25:19:invalid:Using variable 'error' before assignment:CONTROL_FLOW
2+
used-before-assignment:38:14:38:19:invalid_2:Using variable 'error' before assignment:CONTROL_FLOW
3+
used-before-assignment:50:14:50:19:invalid_3:Using variable 'error' before assignment:CONTROL_FLOW
4+
used-before-assignment:60:14:60:19:invalid_4:Using variable 'error' before assignment:CONTROL_FLOW

0 commit comments

Comments
 (0)