<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">On Fri, 8 Apr 2016 at 14:03 Joseph Jevnik <<a href="mailto:joejev@gmail.com">joejev@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>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.</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>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.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div> The goal of this change is to prevent people from relying on names that are defined only in a handler.<br></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>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.</div><div><br></div><div>-Brett</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><br></div>As an example, let's looks at a function with a try except:<br><br><br></div>def f():<br></div>    try:<br>        ...<br></div>    except:<br></div>        a = 1<br></div>    return a<br><br><br></div>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:<br><br><br></div>def g():<br></div>    a = 1<br></div>    try:<br>        ...<br></div>    except:<br></div>        a = 2<br></div>    return a<br><br><br></div>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.<br><br></div>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.<br><br></div>This changes our definition of f from:<br><br>  2           0 SETUP_EXCEPT             8 (to 11)<br><br>  3           3 LOAD_CONST               1 (Ellipsis)<br>              6 POP_TOP<br>              7 POP_BLOCK<br>              8 JUMP_FORWARD            14 (to 25)<br><br>  4     >>   11 POP_TOP<br>             12 POP_TOP<br>             13 POP_TOP<br><br>  5          14 LOAD_CONST               2 (1)<br>             17 STORE_FAST               0 (a)<br>             20 POP_EXCEPT<br>             21 JUMP_FORWARD             1 (to 25)<br>             24 END_FINALLY<br><br>  6     >>   25 LOAD_FAST                0 (a)<br>             28 RETURN_VALUE<br><br></div>to something more like:<br><br>f<br>-<br>  3           0 SETUP_EXCEPT             8 (to 11)<br><br>  4           3 LOAD_CONST               0 (Ellipsis)<br>              6 POP_TOP<br>              7 POP_BLOCK<br>              8 JUMP_FORWARD            20 (to 31)<br><br>  5     >>   11 POP_TOP<br>             12 POP_TOP<br>             13 POP_TOP<br>             14 LOAD_CONST               1 (<code object <excepthandler> at 0x7febcd6e2300, file "<code>", line 1>)<br>             17 LOAD_CONST               2 ('<excepthandler>')<br>             20 MAKE_FUNCTION            0<br>             23 CALL_FUNCTION            0 (0 positional, 0 keyword pair)<br>             26 POP_EXCEPT<br>             27 JUMP_FORWARD             1 (to 31)<br>             30 END_FINALLY<br><br>  7     >>   31 LOAD_FAST                0 (a)<br>             34 RETURN_VALUE<br><br>f.<excepthandler><br>-----------------<br>  1           0 LOAD_CONST               0 (1)<br>              3 STORE_FAST               0 (a)<br>              6 LOAD_CONST               1 (None)<br>              9 RETURN_VALUE<br><br><br></div>This new code properly raises the unbound locals exception when executed. For g we could use a MAKE_CLOSURE instead of MAKE_FUNCTION.<br></div>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a></blockquote></div></div>