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 languagesimplement this kind of construct but have style guides and/or linters thatreject it. I would support a proposal to add this toPEP 8”, and “I note thatthe toy examples are somewhat misleading – the functionality that may be usefulis a conditional return (or break etc.) inside a finally block.”.
This PEP proposes to forbidreturn,break andcontinue statements withinafinally suite where they would break out of thefinally. Their use insuch a location silently cancels any active exception being raised throughthefinally, leading to unclear code and possible bugs.
continue is currently not supported in afinally in Python 3.7 (due toimplementation issues) and the proposal is to not add support for it inPython 3.8. Forreturn andbreak the proposal is to deprecate their usein Python 3.9, emit a compilation warning in Python 3.10 and then forbidtheir use after that.
The use ofreturn,break andcontinue within afinally suite leads to behaviourwhich is not at all obvious. Consider the following function:
deffoo():try:foo()finally:return
This will return cleanly (without an exception) even though it has infiniterecursion and raises an exception within thetry. The reason is that thereturnwithin thefinally will silently cancel any exception that propagates throughthefinally suite. Such behaviour is unexpected and not at all obvious.This function is equivalent to:
deffoo():try:foo()except:passreturn
break andcontinue have similar behaviour (they silence exceptions) if theyjump to code outside thefinally suite. For example:
defbar():whileTrue:try:1/0finally:break
This behaviour goes against the following parts of The Zen of Python:
If this behaviour of silencing exceptions is really needed then the explicitform of a try-except can be used instead, and this makes the code clearer.
Independent to the semantics, implementingreturn/break/continue within afinally suite is non-trivial as it requires to correctly track any activeexceptions at runtime (an executingfinally suite may or may not have anactive exception) and cancel them as appropriate. CPython did have a bug inthis for the case ofcontinue and so originally disallowed it[1]. Requiringcorrect behaviour forreturn/break/continue within afinally puts anunnecessary burden on alternative implementations of Python.
Java allows to return from within afinally block, but its use is discouragedaccording to[2],[3],[4]. The Java compiler later on included a lintingoption-Xlint:finally to warn against the use of return within afinally block.The Eclipse editor also warns about this use.
Ruby allows return from inside ensure (Python’s finally), but it should be anexplicit return. It is discouraged and handled by linters[5],[6].
Like Ruby, JavaScript also allows use ofreturn/break/continue within afinallybut it is seen as unsafe and it is handled by eslint[7].
C# forbids the use of ending statements likereturn/goto/break within afinally[8],[9].
Since the behaviour ofreturn/break/continue within afinally is unclear, thepattern is rarely used, and there is a simple alternative to writing equivalentcode (which is more explicit), forbidding the syntax is the most straightforwardapproach.
This is a change to the compiler, not the grammar. The compiler shouldcheck for the following in afinally suite:
return in any statement, at any level of nesting.break/continue in any statement, at any level of nesting, that wouldtransfer control flow outside thefinally suite.Upon finding such a case it should emit the appropriate exception:
continue, aSyntaxError (this is the current behaviour of 3.7).return/break, aSyntaxWarning in 3.10, and aSyntaxError after that.For example, the following are all forbidden by this proposal:
deff():try:passfinally:returndefg():try:passfinally:try:returnfinally:passdefh():try:passfinally:try:passfinally:forxinrange(10):return
The following is still allowed because thecontinue doesn’t escape thefinally:
try:passfinally:forxinrange(10):continue
Note that yielding from within afinally remains acceptable by this PEPbecause resuming the generator will resume thefinally and eventuallyraise any active exceptions (so they are never silenced by yielding).
This is a backwards incompatible change, forreturn andbreak.
The following locations in the CPython standard library (atv3.8.0b1-651-g7fcc2088a5) usereturn withinfinally:
return withinfinallyreturn withinfinallyThere are no uses ofbreak within afinally (that break out of thefinally)in the standard library.
This is a simplification of the language, and removal of associated code,so should not introduce any new paths for a security exploit.
This feature is very rarely used so forbidding it will likely only impactadvanced users, not beginners and probably not any existing teachingmaterial. Since this is the removal of a feature teaching users will beone by the raising of aSyntaxError if/when the forbidden feature is used.
There is currently no reference implementation, although the way continueis currently handled in afinally (raising aSyntaxError) can be extendedtoreturn andbreak.
This document is placed in the public domain or under theCC0-1.0-Universal license, whichever is more permissive.
Source:https://github.com/python/peps/blob/main/peps/pep-0601.rst
Last modified:2025-02-01 08:55:40 GMT