Skip to content

Commit eba3d3c

Browse files
Fix pylint-dev#85: Emit used-before-assignment in final blocks where try statements could have failed
1 parent 07a635c commit eba3d3c

File tree

5 files changed

+43
-2
lines changed

5 files changed

+43
-2
lines changed

ChangeLog

+7-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ Release date: TBA
99
..
1010
Put new features here and also in 'doc/whatsnew/2.13.rst'
1111

12+
* ``used-before-assignment`` now considers that assignments in the try block
13+
of a try/finally clause may not have occurred when the final block is
14+
executed.
15+
16+
Closes #85
1217

1318
..
1419
Insert your changelog randomly, it will reduce merge conflicts
15-
(Ie. not necessarilly at the end)
20+
(Ie. not necessarily at the end)
1621

1722

1823
What's New in Pylint 2.12.1?
@@ -24,7 +29,7 @@ Release date: TBA
2429

2530
..
2631
Insert your changelog randomly, it will reduce merge conflicts
27-
(Ie. not necessarilly at the end)
32+
(Ie. not necessarily at the end)
2833

2934

3035
What's New in Pylint 2.12.0?

doc/whatsnew/2.13.rst

+6
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ Extensions
1919

2020
Other Changes
2121
=============
22+
23+
* ``used-before-assignment`` now considers that assignments in the try block
24+
of a try/finally clause may not have occurred when the final block is
25+
executed.
26+
27+
Closes #85

pylint/checkers/variables.py

+21
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,8 @@ def mark_as_consumed(self, name, consumed_nodes):
564564
If all of the nodes for the name were consumed, delete the name from
565565
the to_consume dictionary
566566
"""
567+
if name not in self.to_consume:
568+
return
567569
unconsumed = [n for n in self.to_consume[name] if n not in set(consumed_nodes)]
568570
self.consumed[name] = consumed_nodes
569571

@@ -606,6 +608,25 @@ def get_next_to_consume(self, node):
606608
or n.statement().parent_of(node)
607609
]
608610

611+
# If this node is in a Finally block of a Try/Finally,
612+
# filter out assignments in the try portion, assuming they may fail
613+
if (
614+
found_nodes
615+
and isinstance(node.statement().parent, nodes.TryFinally)
616+
and node.statement() in node.statement().parent.finalbody
617+
):
618+
filtered_nodes = [
619+
n
620+
for n in found_nodes
621+
if not (
622+
isinstance(n.statement().parent, nodes.TryFinally)
623+
and n.statement() in n.statement().parent.body
624+
)
625+
]
626+
if not filtered_nodes:
627+
self.mark_as_consumed(name, found_nodes)
628+
return filtered_nodes
629+
609630
return found_nodes
610631

611632

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# pylint: disable=missing-module-docstring, missing-function-docstring
2+
def main():
3+
try:
4+
# throw exception here to make res undefined
5+
res = 1 / 0
6+
res = 42
7+
finally:
8+
print(res) # [used-before-assignment]
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)