[Python-ideas] New scope for exception handlers

Joseph Jevnik joejev at gmail.com
Fri Apr 8 17:03:05 EDT 2016

I would like to propose a change to exception handlers to make it harder to
accidently leak names defined only in the exception handler blocks. This
change follows from the decision to delete the name of an exception at the
end of a handler. The goal of this change is to prevent people from relying
on names that are defined only in a handler.

As an example, let's looks at a function with a try except:

def f():
        a = 1
    return a

This function will only work if the body raises some exception, otherwise
we will get an UnBoundLocalError. I propose that we should make `a` fall
out of scope when we leave the except handler regardless to prevent people
from depending on this behavior. This will make it easier to catch bugs
like this in testing. There is one case where I think the name should not
fall out of scope, and that is when the name is already defined outside of
the handler. For example:

def g():
    a = 1
        a = 2
    return a

I think this code is well behaved and should continue to work as it already
does. There are a couple of ways to implment this new behavior but I think
the simplest way to do this would be to treat the handler as a closure
where all the free variables defined as nonlocal.

This would need to be a small change to the compiler but may have some
performance implications for code that does not hit the except handler. If
the handler is longer than the bytecode needed to create the inner closure
then it may be faster to run the function when the except handler is not

This changes our definition of f from:

  2           0 SETUP_EXCEPT             8 (to 11)

  3           3 LOAD_CONST               1 (Ellipsis)
              6 POP_TOP
              7 POP_BLOCK
              8 JUMP_FORWARD            14 (to 25)

  4     >>   11 POP_TOP
             12 POP_TOP
             13 POP_TOP

  5          14 LOAD_CONST               2 (1)
             17 STORE_FAST               0 (a)
             20 POP_EXCEPT
             21 JUMP_FORWARD             1 (to 25)
             24 END_FINALLY

  6     >>   25 LOAD_FAST                0 (a)
             28 RETURN_VALUE

to something more like:

  3           0 SETUP_EXCEPT             8 (to 11)

  4           3 LOAD_CONST               0 (Ellipsis)
              6 POP_TOP
              7 POP_BLOCK
              8 JUMP_FORWARD            20 (to 31)

  5     >>   11 POP_TOP
             12 POP_TOP
             13 POP_TOP
             14 LOAD_CONST               1 (<code object <excepthandler> at
0x7febcd6e2300, file "<code>", line 1>)
             17 LOAD_CONST               2 ('<excepthandler>')
             20 MAKE_FUNCTION            0
             23 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
             26 POP_EXCEPT
             27 JUMP_FORWARD             1 (to 31)
             30 END_FINALLY

  7     >>   31 LOAD_FAST                0 (a)
             34 RETURN_VALUE

  1           0 LOAD_CONST               0 (1)
              3 STORE_FAST               0 (a)
              6 LOAD_CONST               1 (None)
              9 RETURN_VALUE

This new code properly raises the unbound locals exception when executed.
For g we could use a MAKE_CLOSURE instead of MAKE_FUNCTION.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160408/8aa8882f/attachment.html>

More information about the Python-ideas mailing list