Skip to content

Commit f999497

Browse files
authored
PEP 765: Disallow return/break/continue that exit a finally block (#4119)
1 parent 5795f7b commit f999497

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ peps/pep-0760.rst @pablogsal @brettcannon
641641
peps/pep-0761.rst @sethmlarson @hugovk
642642
peps/pep-0762.rst @pablogsal @ambv @lysnikolaou @emilyemorehouse
643643
peps/pep-0763.rst @dstufft
644+
peps/pep-0765.rst @iritkatriel @ncoghlan
644645
# ...
645646
peps/pep-0777.rst @warsaw
646647
# ...

peps/pep-0765.rst

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
PEP: 765
2+
Title: Disallow return/break/continue that exit a finally block
3+
Author: Irit Katriel <[email protected]>, Alyssa Coghlan <[email protected]>
4+
Discussions-To: https://discuss.python.org/t/an-analysis-of-return-in-finally-in-the-wild/70633
5+
Status: Draft
6+
Type: Standards Track
7+
Created: 15-Nov-2024
8+
Python-Version: 3.14
9+
Post-History: `09-Nov-2024 <https://discuss.python.org/t/an-analysis-of-return-in-finally-in-the-wild/70633>`__,
10+
11+
Abstract
12+
========
13+
14+
This PEP proposes to withdraw support for ``return``, ``break`` and
15+
``continue`` statements that break out of a ``finally`` block.
16+
This was proposed in the past by :pep:`601`. The current PEP
17+
is based on empirical evidence regarding the cost/benefit of
18+
this change, which did not exist at the time that :pep:`601`
19+
was rejected. It also proposes a slightly different solution
20+
than that which was proposed by :pep:`601`.
21+
22+
Motivation
23+
==========
24+
25+
The semantics of ``return``, ``break`` and ``continue`` in a
26+
finally block are surprising for many developers.
27+
The :ref:`documentation <python:tut-cleanup>` mentions that:
28+
29+
- If the ``finally`` clause executes a ``break``, ``continue``
30+
or ``return`` statement, exceptions are not re-raised.
31+
32+
- If a ``finally`` clause includes a ``return`` statement, the
33+
returned value will be the one from the ``finally`` clause’s
34+
``return`` statement, not the value from the ``try`` clause’s
35+
``return`` statement.
36+
37+
Both of these behaviours cause confusion, but the first is
38+
particularly dangerous because a swallowed exception is more
39+
likely to slip through testing, than an incorrect return value.
40+
41+
In 2019, :pep:`601` proposed to change Python to emit a
42+
``SyntaxWarning`` for a few releases and then turn it into a
43+
``SyntaxError``. It was rejected in favour of viewing this
44+
as a programming style issue, to be handled by linters and :pep:`8`.
45+
Indeed, :pep:`8` now recommends not to use control flow statements
46+
in a ``finally`` block, and linters such as
47+
`Pylint <https://pylint.readthedocs.io/en/stable/>`__,
48+
`Ruff <https://docs.astral.sh/ruff/>`__ and
49+
`flake8-bugbear <https://github.com/PyCQA/flake8-bugbear>`__
50+
flag them as a problem.
51+
52+
Rationale
53+
=========
54+
55+
A recent `analysis of real world code
56+
<https://github.com/iritkatriel/finally/blob/main/README.md>`_ shows that:
57+
58+
- These features are rare (2 per million LOC in the top 8,000 PyPI
59+
packages, 4 per million LOC in a random selection of packages).
60+
This could be thanks to the linters that flag this pattern.
61+
- Most of the usages are incorrect, and introduce unintended
62+
exception-swallowing bugs.
63+
- Code owners are typically receptive to fixing the bugs, and
64+
find that easy to do.
65+
66+
This new data indicates that it would benefit Python's users if
67+
Python itself moved them away from this harmful feature.
68+
69+
`One of the arguments brought up
70+
<https://discuss.python.org/t/pep-601-forbid-return-break-continue-breaking-out-of-finally/2239/24>`__
71+
was that language features should be orthogonal, and combine without
72+
context-based restrictions. However, in the meantime :pep:`654` has
73+
been implemented, and it forbids ``return``, ``break`` and ``continue``
74+
in an ``except*`` clause because the semantics of that would violate
75+
the property that ``except*`` clauses operate *in parallel*, so the
76+
code of one clause should not suppress the invocation of another.
77+
In that case we accepted that a combination of features can be
78+
harmful enough that it makes sense to disallow it.
79+
80+
81+
Specification
82+
=============
83+
84+
The change is to specify as part of the language spec that
85+
Python's compiler may emit a ``SyntaxWarning`` or ``SyntaxError``
86+
when a ``return``, ``break`` or ``continue`` would transfer
87+
control flow from within a ``finally`` block to a location outside
88+
of it.
89+
90+
This includes the following examples:
91+
92+
.. code-block::
93+
:class: bad
94+
95+
def f():
96+
try:
97+
...
98+
finally:
99+
return 42
100+
101+
for x in o:
102+
try:
103+
...
104+
finally:
105+
break # (or continue)
106+
107+
But excludes these:
108+
109+
.. code-block::
110+
:class: good
111+
112+
try:
113+
...
114+
finally:
115+
def f():
116+
return 42
117+
118+
try:
119+
...
120+
finally:
121+
for x in o:
122+
break # (or continue)
123+
124+
125+
CPython will emit a ``SyntaxWarning`` in version 3.14, and we leave
126+
it open whether, and when, this will become a ``SyntaxError``.
127+
However, we specify here that a ``SyntaxError`` is permitted by
128+
the language spec, so that other Python implementations can choose
129+
to implement that.
130+
131+
Backwards Compatibility
132+
=======================
133+
134+
For backwards compatibility reasons, we are proposing that CPython
135+
emit only a ``SyntaxWarning``, with no concrete plan to upgrade that
136+
to an error. Code running with ``-We`` may stop working once this
137+
is introduced.
138+
139+
Security Implications
140+
=====================
141+
142+
The warning/error will help programmers avoid some hard to find bugs,
143+
so will have a security benefit. We are not aware of security issues
144+
related to raising a new ``SyntaxWarning`` or ``SyntaxError``.
145+
146+
How to Teach This
147+
=================
148+
149+
The change will be documented in the language spec and in the
150+
What's New documentation. The ``SyntaxWarning`` will alert users
151+
that their code needs to change. The `empirical evidence
152+
<https://github.com/iritkatriel/finally/blob/main/README.md>`__
153+
shows that the changes necessary are typically quite
154+
straightforward.
155+
156+
Copyright
157+
=========
158+
159+
This document is placed in the public domain or under the
160+
CC0-1.0-Universal license, whichever is more permissive.

0 commit comments

Comments
 (0)