classification
Title: interaction of nonlocal and except leading to incorrect behavior
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.4
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: benjamin.peterson, martin.panter, r.david.murray, whitequark, yselivanov
Priority: normal Keywords:

Created on 2015-05-29 01:55 by whitequark, last changed 2015-05-29 11:55 by r.david.murray. This issue is now closed.

Messages (3)
msg244356 - (view) Author: whitequark (whitequark) Date: 2015-05-29 01:55
To reproduce in Python 3.4.2:

def f():
    x = None
    def g():
        nonlocal x
        try:
            raise Exception()
        except Exception as x:
            pass
    g()
    # ↓ UnboundLocalError: local variable 'x' referenced before assignment
    print("x", x)
f()

Compare this to:

def f():
    x = None
    def g():
        nonlocal x
        with open("/dev/null") as x:
            pass
    g()
    print("x", x)
f()

(which prints: x <_io.TextIOWrapper name='/dev/null' mode='r' encoding='UTF-8'>)
msg244357 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-05-29 04:01
The first example seems to behave as I would expect. The UnboundLocalError is raised by the print() call, because the “x” variable has been deleted by the exception handler. Equivalent code without using “nonlocal”:

>>> def f():
...     x = None
...     try:
...         raise Exception()
...     except Exception as x:
...         pass
...     print("x", x)  # UnboundLocal due to exception handler
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in f
UnboundLocalError: local variable 'x' referenced before assignment

In both cases, I think this is correct behaviour. See <https://docs.python.org/3.4/reference/compound_stmts.html#except>, which says “When an exception has been assigned using ‘as target’, it is cleared at the end of the except clause.”
msg244368 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-05-29 11:55
Indeed, if you replace the except clause with a 'del x', you get the same UnboundLocalError.  This is working as designed.
History
Date User Action Args
2015-05-29 11:55:17r.david.murraysetnosy: + r.david.murray
messages: + msg244368
2015-05-29 04:44:26ned.deilysetstatus: open -> closed
stage: resolved
2015-05-29 04:01:17martin.pantersetresolution: not a bug

messages: + msg244357
nosy: + martin.panter
2015-05-29 02:06:21eric.snowsetnosy: + benjamin.peterson
2015-05-29 01:59:44yselivanovsetnosy: + yselivanov
2015-05-29 01:55:26whitequarkcreate