detect "referenced before assignment" variables

Bug #1522024 reported by spaceone
10
This bug affects 2 people
Affects Status Importance Assigned to Milestone
Pyflakes
New
Undecided
Unassigned

Bug Description

Assume the following code:

try:
    fd = open(filename, 'r')
finally:
    if fd is not None:
        fd.close()

This would raise "UnboundLocalError: local variable 'fd' referenced before assignment" if opening the file fails e.g. because of no permissions/not existing files/etc..
It would be very nice if pyflakes detect this kind of error and warn about the use of 'fd' in the finally block if 'fd' is created inside of the try-block.

Revision history for this message
John Vandenberg (jayvdb) wrote :

Note that this would cause errors for 'valid' code, when finally referenced a variable created only in try: which *may* have been bound before the exception. The coder may know 99% that the variable created in the try block will never fail (except maybe out of memory errors, I suppose). e.g. something like

----
try:
    bar = foo.x
    baz = bar.y
finally:
    if bar is not None:
        bar.z
----

Anyway, it is extremely prone to bugs, so IMO it is acceptable to error in that scenario.

I suspect this is quite easy to solve moderately well.

Instead of simply processing all TRY children nodes sequentially

https://github.com/pyflakes/pyflakes/blob/master/pyflakes/checker.py#L1092

process `finally:` children first, and then the `try:`, `except:` and `else:` children.

The better approach would be to save a copy of the contents of the scope before processing the `try:` children nodes, and use that saved copy of the scope while processing the `finally:` children. That way it is possible to check whether the referenced names were created during the `try:` branch.

Revision history for this message
asmeurer (asmeurer) wrote :

I think it's a good idea. I've been bitten by it before with something like

try:
    p = subprocess.Popen([cmd])
    p.communicate()
finally:
    return p.returncode

where cmd is not found. It's worse in Python 2 where the exceptions don't chain, but a NameError or UnboundLocalError looks bad regardless. It's also a good reason to use context managers instead of try/finally when possible, and to minimize the code that is inside of a try block.

But I suppose it would be good to also check some real code for potential false positives.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.