Skip to content

Commit 228f56f

Browse files
Fix pylint-dev#85, pylint-dev#2615: Emit used-before-assignment in final or except blocks where try statements could have failed
1 parent bd55b27 commit 228f56f

7 files changed

+75
-1
lines changed

ChangeLog

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

1717
Closes #4761
1818

19+
* ``used-before-assignment`` now considers that assignments in a try block
20+
may not have occurred when the except or finally blocks are executed.
21+
22+
Closes #85, #2615
23+
1924
* ``used-before-assignment`` now checks names in try blocks.
2025

2126
* Some files in ``pylint.testutils`` were deprecated. In the future imports should be done from the

doc/whatsnew/2.13.rst

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ Other Changes
3636

3737
Closes #4761
3838

39+
* ``used-before-assignment`` now considers that assignments in a try block
40+
may not have occurred when the except or finally blocks are executed.
41+
42+
Closes #85, #2615
43+
3944
* ``used-before-assignment`` now checks names in try blocks.
4045

4146
* Require Python ``3.6.2`` to run pylint.

pylint/checkers/variables.py

+45-1
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ def _has_locals_call_after_node(stmt, scope):
405405
"E0601": (
406406
"Using variable %r before assignment",
407407
"used-before-assignment",
408-
"Used when a local variable is accessed before its assignment.",
408+
"Used when a local variable is accessed before its assignment or in "
409+
"except or finally blocks without being defined before the try block.",
409410
),
410411
"E0602": (
411412
"Undefined variable %r",
@@ -662,6 +663,49 @@ def get_next_to_consume(self, node):
662663
self.consumed_uncertain[node.name] += difference
663664
found_nodes = filtered_nodes
664665

666+
# If this node is in a Finally block of a Try/Finally,
667+
# filter out assignments in the try portion, assuming they may fail
668+
if (
669+
found_nodes
670+
and isinstance(node.statement(future=True).parent, nodes.TryFinally)
671+
and node.statement(future=True)
672+
in node.statement(future=True).parent.finalbody
673+
):
674+
filtered_nodes = [
675+
n
676+
for n in found_nodes
677+
if not (
678+
n.statement(future=True).parent
679+
is node.statement(future=True).parent
680+
and n.statement(future=True) in n.statement(future=True).parent.body
681+
)
682+
]
683+
filtered_nodes_set = set(filtered_nodes)
684+
difference = [n for n in found_nodes if n not in filtered_nodes_set]
685+
self.consumed_uncertain[node.name] += difference
686+
found_nodes = filtered_nodes
687+
688+
# If this node is in an ExceptHandler,
689+
# filter out assignments in the try portion, assuming they may fail
690+
if found_nodes and isinstance(
691+
node.statement(future=True).parent, nodes.ExceptHandler
692+
):
693+
filtered_nodes = [
694+
n
695+
for n in found_nodes
696+
if not (
697+
isinstance(n.statement(future=True).parent, nodes.TryExcept)
698+
and n.statement(future=True) in n.statement(future=True).parent.body
699+
and node.statement(future=True).parent
700+
in n.statement(future=True).parent.handlers
701+
and n.statement(future=True) in n.statement(future=True).parent.body
702+
)
703+
]
704+
filtered_nodes_set = set(filtered_nodes)
705+
difference = [n for n in found_nodes if n not in filtered_nodes_set]
706+
self.consumed_uncertain[node.name] += difference
707+
found_nodes = filtered_nodes
708+
665709
return found_nodes
666710

667711

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""https://github.com/PyCQA/pylint/issues/2615"""
2+
def main():
3+
"""When evaluating except blocks, assume try statements fail."""
4+
try:
5+
res = 1 / 0
6+
res = 42
7+
except ZeroDivisionError:
8+
print(res) # [used-before-assignment]
9+
print(res)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
used-before-assignment:8:14:8:17:main:Using variable 'res' before assignment:UNDEFINED
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""https://github.com/PyCQA/pylint/issues/85"""
2+
def main():
3+
"""When evaluating finally blocks, assume try statements fail."""
4+
try:
5+
res = 1 / 0
6+
res = 42
7+
finally:
8+
print(res) # [used-before-assignment]
9+
print(res)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
used-before-assignment:8:14:8:17:main:Using variable 'res' before assignment:UNDEFINED

0 commit comments

Comments
 (0)