2019-09-01 11:25:17 -04:00
|
|
|
|
PEP: 601
|
|
|
|
|
Title: Forbid return/break/continue breaking out of finally
|
2021-03-12 14:40:40 -05:00
|
|
|
|
Author: Damien George, Batuhan Taskaya
|
2023-10-11 08:05:51 -04:00
|
|
|
|
Sponsor: Alyssa Coghlan
|
2019-09-09 11:16:32 -04:00
|
|
|
|
Discussions-To: https://discuss.python.org/t/pep-601-forbid-return-break-continue-breaking-out-of-finally/2239
|
2019-09-23 10:13:10 -04:00
|
|
|
|
Status: Rejected
|
2019-09-01 11:25:17 -04:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 26-Aug-2019
|
|
|
|
|
Python-Version: 3.8
|
2022-03-09 11:04:44 -05:00
|
|
|
|
Post-History: 26-Aug-2019, 23-Sep-2019
|
2019-09-23 10:13:10 -04:00
|
|
|
|
Resolution: https://discuss.python.org/t/pep-601-forbid-return-break-continue-breaking-out-of-finally/2239/32
|
|
|
|
|
|
|
|
|
|
Rejection Note
|
|
|
|
|
==============
|
|
|
|
|
|
|
|
|
|
This PEP was rejected by the Steering Council by a vote of 4/4.
|
|
|
|
|
|
|
|
|
|
Guido's arguments for rejecting the PEP are: "it seems to me that most languages
|
|
|
|
|
implement this kind of construct but have style guides and/or linters that
|
2022-01-21 06:03:51 -05:00
|
|
|
|
reject it. I would support a proposal to add this to :pep:`8`", and "I note that
|
2019-09-23 10:13:10 -04:00
|
|
|
|
the toy examples are somewhat misleading – the functionality that may be useful
|
|
|
|
|
is a conditional return (or break etc.) inside a finally block.".
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
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
|
2019-09-01 11:25:17 -04:00
|
|
|
|
such a location silently cancels any active exception being raised through
|
2022-07-28 05:41:30 -04:00
|
|
|
|
the ``finally``, leading to unclear code and possible bugs.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
``continue`` is currently not supported in a ``finally`` in Python 3.7 (due to
|
2019-09-01 11:25:17 -04:00
|
|
|
|
implementation issues) and the proposal is to not add support for it in
|
2022-07-28 05:41:30 -04:00
|
|
|
|
Python 3.8. For ``return`` and ``break`` the proposal is to deprecate their use
|
2019-09-01 11:25:17 -04:00
|
|
|
|
in Python 3.9, emit a compilation warning in Python 3.10 and then forbid
|
|
|
|
|
their use after that.
|
|
|
|
|
|
|
|
|
|
Motivation
|
|
|
|
|
==========
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
The use of ``return``, ``break`` and ``continue`` within a ``finally`` suite leads to behaviour
|
2019-09-01 11:25:17 -04:00
|
|
|
|
which is not at all obvious. Consider the following function::
|
|
|
|
|
|
|
|
|
|
def foo():
|
|
|
|
|
try:
|
|
|
|
|
foo()
|
|
|
|
|
finally:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
This will return cleanly (without an exception) even though it has infinite
|
2022-07-28 05:41:30 -04:00
|
|
|
|
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.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
This function is equivalent to::
|
|
|
|
|
|
|
|
|
|
def foo():
|
|
|
|
|
try:
|
|
|
|
|
foo()
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
return
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
``break`` and ``continue`` have similar behaviour (they silence exceptions) if they
|
|
|
|
|
jump to code outside the ``finally`` suite. For example::
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
def bar():
|
|
|
|
|
while True:
|
|
|
|
|
try:
|
|
|
|
|
1 / 0
|
|
|
|
|
finally:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
This behaviour goes against the following parts of The Zen of Python:
|
|
|
|
|
|
|
|
|
|
* Explicit is better than implicit - exceptions are implicitly silenced
|
|
|
|
|
|
|
|
|
|
* Readability counts - the intention of the code is not obvious
|
|
|
|
|
|
|
|
|
|
* Errors should never pass silently; Unless explicitly silenced - exceptions
|
|
|
|
|
are implicitly silenced
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
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
|
2019-09-01 11:25:17 -04:00
|
|
|
|
active exception) and cancel them as appropriate. CPython did have a bug in
|
2022-07-28 05:41:30 -04:00
|
|
|
|
this for the case of ``continue`` and so originally disallowed it [1]_. Requiring
|
|
|
|
|
correct behaviour for ``return``/``break``/``continue`` within a ``finally`` puts an
|
2019-09-01 11:25:17 -04:00
|
|
|
|
unnecessary burden on alternative implementations of Python.
|
|
|
|
|
|
|
|
|
|
Other languages
|
|
|
|
|
===============
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
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.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
The Eclipse editor also warns about this use.
|
|
|
|
|
|
|
|
|
|
Ruby allows return from inside ensure (Python's finally), but it should be an
|
2022-07-28 05:41:30 -04:00
|
|
|
|
explicit return. It is discouraged and handled by linters [5]_, [6]_.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
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]_.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
C# forbids the use of ending statements like ``return``/``goto``/``break`` within a ``finally``
|
|
|
|
|
[8]_, [9]_.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
Since the behaviour of ``return``/``break``/``continue`` within a ``finally`` is unclear, the
|
2019-09-01 11:25:17 -04:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
This is a change to the compiler, not the grammar. The compiler should
|
|
|
|
|
check for the following in a ``finally`` suite:
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
* A ``return`` in any statement, at any level of nesting.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
* A ``break``/``continue`` in any statement, at any level of nesting, that would
|
|
|
|
|
transfer control flow outside the ``finally`` suite.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
Upon finding such a case it should emit the appropriate exception:
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
* For ``continue``, a ``SyntaxError`` (this is the current behaviour of 3.7).
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
* For ``return``/``break``, a ``SyntaxWarning`` in 3.10, and a ``SyntaxError`` after that.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
For example, the following are all forbidden by this proposal::
|
|
|
|
|
|
|
|
|
|
def f():
|
|
|
|
|
try:
|
|
|
|
|
pass
|
|
|
|
|
finally:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
def g():
|
|
|
|
|
try:
|
|
|
|
|
pass
|
|
|
|
|
finally:
|
|
|
|
|
try:
|
|
|
|
|
return
|
|
|
|
|
finally:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def h():
|
|
|
|
|
try:
|
|
|
|
|
pass
|
|
|
|
|
finally:
|
|
|
|
|
try:
|
|
|
|
|
pass
|
|
|
|
|
finally:
|
|
|
|
|
for x in range(10):
|
|
|
|
|
return
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
The following is still allowed because the ``continue`` doesn't escape the
|
|
|
|
|
``finally``::
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
pass
|
|
|
|
|
finally:
|
|
|
|
|
for x in range(10):
|
|
|
|
|
continue
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
Note that yielding from within a ``finally`` remains acceptable by this PEP
|
|
|
|
|
because resuming the generator will resume the ``finally`` and eventually
|
2019-09-01 11:25:17 -04:00
|
|
|
|
raise any active exceptions (so they are never silenced by yielding).
|
|
|
|
|
|
|
|
|
|
Backwards Compatibility
|
|
|
|
|
=======================
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
This is a backwards incompatible change, for ``return`` and ``break``.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
The following locations in the CPython standard library (at
|
2022-07-28 05:41:30 -04:00
|
|
|
|
v3.8.0b1-651-g7fcc2088a5) use ``return`` within ``finally``:
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
* Lib/subprocess.py:921 - the use here looks like a bug
|
|
|
|
|
|
|
|
|
|
* Lib/multiprocessing/connection.py:316 - the use here looks legitimate
|
|
|
|
|
but the intention is not clear
|
|
|
|
|
|
|
|
|
|
* Lib/multiprocessing/connection.py:318 - the use here looks legitimate
|
|
|
|
|
but the intention is not clear
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
* Lib/test/test_sys_settrace.py:837 - a test for ``return`` within ``finally``
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
* Lib/test/test_sys_settrace.py:1346 - a test for ``return`` within ``finally``
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
There are no uses of ``break`` within a ``finally`` (that break out of the ``finally``)
|
2019-09-01 11:25:17 -04:00
|
|
|
|
in the standard library.
|
|
|
|
|
|
|
|
|
|
Security Implications
|
|
|
|
|
=====================
|
|
|
|
|
|
|
|
|
|
This is a simplification of the language, and removal of associated code,
|
|
|
|
|
so should not introduce any new paths for a security exploit.
|
|
|
|
|
|
|
|
|
|
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
|
2022-07-28 05:41:30 -04:00
|
|
|
|
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.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
Reference Implementation
|
|
|
|
|
========================
|
|
|
|
|
|
|
|
|
|
There is currently no reference implementation, although the way continue
|
2022-07-28 05:41:30 -04:00
|
|
|
|
is currently handled in a ``finally`` (raising a ``SyntaxError``) can be extended
|
|
|
|
|
to ``return`` and ``break``.
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
.. [1] https://github.com/python/cpython/issues/82011
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
.. [2] https://stackoverflow.com/questions/48088/returning-from-a-finally-block-in-java
|
|
|
|
|
|
|
|
|
|
.. [3] https://web.archive.org/web/20070922061412/http://weblogs.java.net/blog/staufferjames/archive/2007/06/_dont_return_in.html
|
|
|
|
|
|
|
|
|
|
.. [4] https://wiki.sei.cmu.edu/confluence/display/java/ERR04-J.+Do+not+complete+abruptly+from+a+finally+block
|
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
.. [5] https://github.com/rubocop/rubocop/issues/5949
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2022-07-28 05:41:30 -04:00
|
|
|
|
.. [6] https://www.rubydoc.info/gems/rubocop/0.74.0/RuboCop/Cop/Lint/EnsureReturn
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2019-09-05 08:15:56 -04:00
|
|
|
|
.. [7] https://eslint.org/docs/rules/no-unsafe-finally
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2019-09-05 08:15:56 -04:00
|
|
|
|
.. [8] https://social.msdn.microsoft.com/Forums/vstudio/en-US/87faf259-3c54-4f3a-8d2b-ff82de44992f/return-statement-in-finally-block?forum=netfxbcl
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
2019-09-05 08:15:56 -04:00
|
|
|
|
.. [9] https://stackoverflow.com/a/5788268
|
2019-09-01 11:25:17 -04:00
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document is placed in the public domain or under the
|
|
|
|
|
CC0-1.0-Universal license, whichever is more permissive.
|