[Python-ideas] New scope for exception handlers

Brett Cannon brett at python.org
Fri Apr 8 18:14:24 EDT 2016


On Fri, 8 Apr 2016 at 14:03 Joseph Jevnik <joejev at gmail.com> wrote:

> I would like to propose a change to exception handlers to make it harder
> to accidentally 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.
>

So that change was to prevent memory leaks for the caught exception due to
tracebacks being attached to exceptions, not with anything to do with
scoping.


> The goal of this change is to prevent people from relying on names that
> are defined only in a handler.
>

So that will break code and would make an `except` clause even more special
in terms of how it works compared to other blocks. Right now the Python
scoping rules are pretty straight-forward (local, non-local/free, global,
built-in). Adding this would shove in a "unless you're in an `except`
clause" rule that makes things more complicated for little benefit in the
face of backwards-compatibility.

-Brett


>
> As an example, let's looks at a function with a try except:
>
>
> def f():
>     try:
>         ...
>     except:
>         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
>     try:
>         ...
>     except:
>         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
> hit.
>
> 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:
>
> f
> -
>   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
>
> f.<excepthandler>
> -----------------
>   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.
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160408/e7e6b30b/attachment-0001.html>


More information about the Python-ideas mailing list