diff --git a/pep-0601.txt b/pep-0601.txt index d0046a6c0..046c898b2 100644 --- a/pep-0601.txt +++ b/pep-0601.txt @@ -25,21 +25,21 @@ is a conditional return (or break etc.) inside a finally block.". Abstract ======== -This PEP proposes to forbid return, break and continue statements within -a finally suite where they would break out of the finally. Their use in +This PEP proposes to forbid ``return``, ``break`` and ``continue`` statements within +a ``finally`` suite where they would break out of the ``finally``. Their use in such a location silently cancels any active exception being raised through -the finally, leading to unclear code and possible bugs. +the ``finally``, leading to unclear code and possible bugs. -Continue is currently not supported in a finally in Python 3.7 (due to +``continue`` is currently not supported in a ``finally`` in Python 3.7 (due to implementation issues) and the proposal is to not add support for it in -Python 3.8. For return and break the proposal is to deprecate their use +Python 3.8. For ``return`` and ``break`` the proposal is to deprecate their use in Python 3.9, emit a compilation warning in Python 3.10 and then forbid their use after that. Motivation ========== -The use of return, break and continue within a finally suite leads to behaviour +The use of ``return``, ``break`` and ``continue`` within a ``finally`` suite leads to behaviour which is not at all obvious. Consider the following function:: def foo(): @@ -49,9 +49,9 @@ which is not at all obvious. Consider the following function:: return This will return cleanly (without an exception) even though it has infinite -recursion and raises an exception within the try. The reason is that the return -within the finally will silently cancel any exception that propagates through -the finally suite. Such behaviour is unexpected and not at all obvious. +recursion and raises an exception within the ``try``. The reason is that the ``return`` +within the ``finally`` will silently cancel any exception that propagates through +the ``finally`` suite. Such behaviour is unexpected and not at all obvious. This function is equivalent to:: def foo(): @@ -61,8 +61,8 @@ This function is equivalent to:: pass return -Break and continue have similar behaviour (they silence exceptions) if they -jump to code outside the finally suite. For example:: +``break`` and ``continue`` have similar behaviour (they silence exceptions) if they +jump to code outside the ``finally`` suite. For example:: def bar(): while True: @@ -83,35 +83,35 @@ This behaviour goes against the following parts of The Zen of Python: If this behaviour of silencing exceptions is really needed then the explicit form of a try-except can be used instead, and this makes the code clearer. -Independent to the semantics, implementing return/break/continue within a -finally suite is non-trivial as it requires to correctly track any active -exceptions at runtime (an executing finally suite may or may not have an +Independent to the semantics, implementing ``return``/``break``/``continue`` within a +``finally`` suite is non-trivial as it requires to correctly track any active +exceptions at runtime (an executing ``finally`` suite may or may not have an active exception) and cancel them as appropriate. CPython did have a bug in -this for the case of continue and so originally disallowed it [1]. Requiring -correct behaviour for return/break/continue within a finally puts an +this for the case of ``continue`` and so originally disallowed it [1]_. Requiring +correct behaviour for ``return``/``break``/``continue`` within a ``finally`` puts an unnecessary burden on alternative implementations of Python. Other languages =============== -Java allows to return from within a finally block, but its use is discouraged -according to [2], [3], [4]. The Java compiler later on included a linting -option -Xlint:finally to warn against the use of return within a finally block. +Java allows to return from within a ``finally`` block, but its use is discouraged +according to [2]_, [3]_, [4]_. The Java compiler later on included a linting +option ``-Xlint:finally`` to warn against the use of return within a ``finally`` block. The Eclipse editor also warns about this use. Ruby allows return from inside ensure (Python's finally), but it should be an -explicit return. It is discouraged and handled by linters [5], [6]. +explicit return. It is discouraged and handled by linters [5]_, [6]_. -Like Ruby, JavaScript also allows use of return/break/continue within a finally -but it is seen as unsafe and it is handled by eslint [7]. +Like Ruby, JavaScript also allows use of ``return``/``break``/``continue`` within a ``finally`` +but it is seen as unsafe and it is handled by eslint [7]_. -C# forbids the use of ending statements like return/goto/break within a finally -[8], [9]. +C# forbids the use of ending statements like ``return``/``goto``/``break`` within a ``finally`` +[8]_, [9]_. Rationale ========= -Since the behaviour of return/break/continue within a finally is unclear, the +Since the behaviour of ``return``/``break``/``continue`` within a ``finally`` is unclear, the pattern is rarely used, and there is a simple alternative to writing equivalent code (which is more explicit), forbidding the syntax is the most straightforward approach. @@ -119,19 +119,19 @@ approach. Specification ============= -This is a change to the compiler, not the grammar. The compiler should -check for the following in a finally suite: +This is a change to the compiler, not the grammar. The compiler should +check for the following in a ``finally`` suite: -* A return in any statement, at any level of nesting. +* A ``return`` in any statement, at any level of nesting. -* A break/continue in any statement, at any level of nesting, that would - transfer control flow outside the finally suite. +* A ``break``/``continue`` in any statement, at any level of nesting, that would + transfer control flow outside the ``finally`` suite. Upon finding such a case it should emit the appropriate exception: -* For continue, a SyntaxError (this is the current behaviour of 3.7). +* For ``continue``, a ``SyntaxError`` (this is the current behaviour of 3.7). -* For return/break, a SyntaxWarning in 3.10, and a SyntaxError after that. +* For ``return``/``break``, a ``SyntaxWarning`` in 3.10, and a ``SyntaxError`` after that. For example, the following are all forbidden by this proposal:: @@ -160,8 +160,8 @@ For example, the following are all forbidden by this proposal:: for x in range(10): return -The following is still allowed because the continue doesn't escape the -finally:: +The following is still allowed because the ``continue`` doesn't escape the +``finally``:: try: pass @@ -169,17 +169,17 @@ finally:: for x in range(10): continue -Note that yielding from within a finally remains acceptable by this PEP -because resuming the generator will resume the finally and eventually +Note that yielding from within a ``finally`` remains acceptable by this PEP +because resuming the generator will resume the ``finally`` and eventually raise any active exceptions (so they are never silenced by yielding). Backwards Compatibility ======================= -This is a backwards incompatible change, for return and break. +This is a backwards incompatible change, for ``return`` and ``break``. The following locations in the CPython standard library (at -v3.8.0b1-651-g7fcc2088a5) use return within finally: +v3.8.0b1-651-g7fcc2088a5) use ``return`` within ``finally``: * Lib/subprocess.py:921 - the use here looks like a bug @@ -189,11 +189,11 @@ v3.8.0b1-651-g7fcc2088a5) use return within finally: * Lib/multiprocessing/connection.py:318 - the use here looks legitimate but the intention is not clear -* Lib/test/test_sys_settrace.py:837 - a test for return within finally +* Lib/test/test_sys_settrace.py:837 - a test for ``return`` within ``finally`` -* Lib/test/test_sys_settrace.py:1346 - a test for return within finally +* Lib/test/test_sys_settrace.py:1346 - a test for ``return`` within ``finally`` -There are no uses of break within a finally (that break out of the finally) +There are no uses of ``break`` within a ``finally`` (that break out of the ``finally``) in the standard library. Security Implications @@ -207,20 +207,20 @@ How to Teach This This feature is very rarely used so forbidding it will likely only impact advanced users, not beginners and probably not any existing teaching -material. Since this is the removal of a feature teaching users will be -one by the raising of a SyntaxError if/when the forbidden feature is used. +material. Since this is the removal of a feature teaching users will be +one by the raising of a ``SyntaxError`` if/when the forbidden feature is used. Reference Implementation ======================== There is currently no reference implementation, although the way continue -is currently handled in a finally (raising a SyntaxError) can be extended -to return and break. +is currently handled in a ``finally`` (raising a ``SyntaxError``) can be extended +to ``return`` and ``break``. References ========== -.. [1] https://bugs.python.org/issue37830 +.. [1] https://github.com/python/cpython/issues/82011 .. [2] https://stackoverflow.com/questions/48088/returning-from-a-finally-block-in-java @@ -228,9 +228,9 @@ References .. [4] https://wiki.sei.cmu.edu/confluence/display/java/ERR04-J.+Do+not+complete+abruptly+from+a+finally+block -.. [5] https://github.com/rubocop-hq/rubocop/issues/5949 +.. [5] https://github.com/rubocop/rubocop/issues/5949 -.. [6] https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Lint/EnsureReturn +.. [6] https://www.rubydoc.info/gems/rubocop/0.74.0/RuboCop/Cop/Lint/EnsureReturn .. [7] https://eslint.org/docs/rules/no-unsafe-finally @@ -243,14 +243,3 @@ Copyright This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive. - - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: