CINXE.COM
PEP 765 – Disallow return/break/continue that exit a finally block | peps.python.org
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 765 – Disallow return/break/continue that exit a finally block | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0765/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 765 – Disallow return/break/continue that exit a finally block | peps.python.org'> <meta property="og:description" content="This PEP proposes to withdraw support for return, break and continue statements that break out of a finally block. This was proposed in the past by PEP 601. The current PEP is based on empirical evidence regarding the cost/benefit of this change, which ..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0765/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="This PEP proposes to withdraw support for return, break and continue statements that break out of a finally block. This was proposed in the past by PEP 601. The current PEP is based on empirical evidence regarding the cost/benefit of this change, which ..."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li> <li><a href="../pep-0000/">PEP Index</a> » </li> <li>PEP 765</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 765 – Disallow return/break/continue that exit a finally block</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Irit Katriel <irit at python.org>, Alyssa Coghlan <ncoghlan at gmail.com></dd> <dt class="field-even">Discussions-To<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348">Discourse thread</a></dd> <dt class="field-odd">Status<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-even">Type<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-odd">Created<span class="colon">:</span></dt> <dd class="field-odd">15-Nov-2024</dd> <dt class="field-even">Python-Version<span class="colon">:</span></dt> <dd class="field-even">3.14</dd> <dt class="field-odd">Post-History<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/an-analysis-of-return-in-finally-in-the-wild/70633" title="Discourse thread">09-Nov-2024</a>, <a class="reference external" href="https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348" title="Discourse thread">16-Nov-2024</a></dd> <dt class="field-even">Replaces<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="../pep-0601/">601</a></dd> <dt class="field-odd">Resolution<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/71348/111">Discourse message</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a></li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#security-implications">Security Implications</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#emit-syntaxerror-in-cpython">Emit <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> in CPython</a></li> <li><a class="reference internal" href="#change-semantics">Change Semantics</a></li> </ul> </li> <li><a class="reference internal" href="#appendix">Appendix</a><ul> <li><a class="reference internal" href="#return-in-finally-considered-harmful"><code class="docutils literal notranslate"><span class="pre">return</span></code> in <code class="docutils literal notranslate"><span class="pre">finally</span></code> considered harmful</a><ul> <li><a class="reference internal" href="#method">Method</a></li> <li><a class="reference internal" href="#results">Results</a></li> <li><a class="reference internal" href="#discussion">Discussion</a></li> </ul> </li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <div class="pep-banner canonical-doc sticky-banner admonition important"> <p class="admonition-title">Important</p> <p>This PEP is a historical document. The up-to-date, canonical documentation can now be found at <a class="reference external" href="https://docs.python.org/3.14/reference/compound_stmts.html#finally" title="(in Python v3.14)"><span>finally clause</span></a>.</p> <p class="close-button">×</p> <p>See <a class="pep reference internal" href="../pep-0001/" title="PEP 1 – PEP Purpose and Guidelines">PEP 1</a> for how to propose changes.</p> </div> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP proposes to withdraw support for <code class="docutils literal notranslate"><span class="pre">return</span></code>, <code class="docutils literal notranslate"><span class="pre">break</span></code> and <code class="docutils literal notranslate"><span class="pre">continue</span></code> statements that break out of a <code class="docutils literal notranslate"><span class="pre">finally</span></code> block. This was proposed in the past by <a class="pep reference internal" href="../pep-0601/" title="PEP 601 – Forbid return/break/continue breaking out of finally">PEP 601</a>. The current PEP is based on empirical evidence regarding the cost/benefit of this change, which did not exist at the time that <a class="pep reference internal" href="../pep-0601/" title="PEP 601 – Forbid return/break/continue breaking out of finally">PEP 601</a> was rejected. It also proposes a slightly different solution than that which was proposed by <a class="pep reference internal" href="../pep-0601/" title="PEP 601 – Forbid return/break/continue breaking out of finally">PEP 601</a>.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>The semantics of <code class="docutils literal notranslate"><span class="pre">return</span></code>, <code class="docutils literal notranslate"><span class="pre">break</span></code> and <code class="docutils literal notranslate"><span class="pre">continue</span></code> in a finally block are surprising for many developers. The <a class="reference external" href="https://docs.python.org/3/tutorial/errors.html#tut-cleanup" title="(in Python v3.13)"><span class="xref std std-ref">documentation</span></a> mentions that:</p> <ul class="simple"> <li>If the <code class="docutils literal notranslate"><span class="pre">finally</span></code> clause executes a <code class="docutils literal notranslate"><span class="pre">break</span></code>, <code class="docutils literal notranslate"><span class="pre">continue</span></code> or <code class="docutils literal notranslate"><span class="pre">return</span></code> statement, exceptions are not re-raised.</li> <li>If a <code class="docutils literal notranslate"><span class="pre">finally</span></code> clause includes a <code class="docutils literal notranslate"><span class="pre">return</span></code> statement, the returned value will be the one from the <code class="docutils literal notranslate"><span class="pre">finally</span></code> clause’s <code class="docutils literal notranslate"><span class="pre">return</span></code> statement, not the value from the <code class="docutils literal notranslate"><span class="pre">try</span></code> clause’s <code class="docutils literal notranslate"><span class="pre">return</span></code> statement.</li> </ul> <p>Both of these behaviours cause confusion, but the first is particularly dangerous because a swallowed exception is more likely to slip through testing, than an incorrect return value.</p> <p>In 2019, <a class="pep reference internal" href="../pep-0601/" title="PEP 601 – Forbid return/break/continue breaking out of finally">PEP 601</a> proposed to change Python to emit a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> for a few releases and then turn it into a <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code>. It was rejected in favour of viewing this as a programming style issue, to be handled by linters and <a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a>. Indeed, <a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a> now recommends not to use control flow statements in a <code class="docutils literal notranslate"><span class="pre">finally</span></code> block, and linters such as <a class="reference external" href="https://pylint.readthedocs.io/en/stable/">Pylint</a>, <a class="reference external" href="https://docs.astral.sh/ruff/">Ruff</a> and <a class="reference external" href="https://github.com/PyCQA/flake8-bugbear">flake8-bugbear</a> flag them as a problem.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>A recent <a class="reference external" href="https://github.com/iritkatriel/finally/blob/main/README.md">analysis of real world code</a> shows that:</p> <ul class="simple"> <li>These features are rare (2 per million LOC in the top 8,000 PyPI packages, 4 per million LOC in a random selection of packages). This could be thanks to the linters that flag this pattern.</li> <li>Most of the usages are incorrect, and introduce unintended exception-swallowing bugs.</li> <li>Code owners are typically receptive to fixing the bugs, and find that easy to do.</li> </ul> <p>See <a class="reference external" href="#appendix">the appendix</a> for more details.</p> <p>This new data indicates that it would benefit Python’s users if Python itself moved them away from this harmful feature.</p> <p>One of the arguments brought up in <a class="reference external" href="https://discuss.python.org/t/pep-601-forbid-return-break-continue-breaking-out-of-finally/2239/24">the PEP 601 discussion</a> was that language features should be orthogonal, and combine without context-based restrictions. However, in the meantime <a class="pep reference internal" href="../pep-0654/" title="PEP 654 – Exception Groups and except*">PEP 654</a> has been implemented, and it forbids <code class="docutils literal notranslate"><span class="pre">return</span></code>, <code class="docutils literal notranslate"><span class="pre">break</span></code> and <code class="docutils literal notranslate"><span class="pre">continue</span></code> in an <code class="docutils literal notranslate"><span class="pre">except*</span></code> clause because the semantics of that would violate the property that <code class="docutils literal notranslate"><span class="pre">except*</span></code> clauses operate <em>in parallel</em>, so the code of one clause should not suppress the invocation of another. In that case we accepted that a combination of features can be harmful enough that it makes sense to disallow it.</p> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>The change is to specify as part of the language spec that Python’s compiler may emit a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> or <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> when a <code class="docutils literal notranslate"><span class="pre">return</span></code>, <code class="docutils literal notranslate"><span class="pre">break</span></code> or <code class="docutils literal notranslate"><span class="pre">continue</span></code> would transfer control flow from within a <code class="docutils literal notranslate"><span class="pre">finally</span></code> block to a location outside of it.</p> <p>These examples may emit a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> or <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code>:</p> <div class="bad highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span> <span class="k">try</span><span class="p">:</span> <span class="o">...</span> <span class="k">finally</span><span class="p">:</span> <span class="k">return</span> <span class="mi">42</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">o</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="o">...</span> <span class="k">finally</span><span class="p">:</span> <span class="k">break</span> <span class="c1"># (or continue)</span> </pre></div> </div> <p>These examples would not emit the warning or error:</p> <div class="good highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="k">try</span><span class="p">:</span> <span class="o">...</span> <span class="k">finally</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span> <span class="k">return</span> <span class="mi">42</span> <span class="k">try</span><span class="p">:</span> <span class="o">...</span> <span class="k">finally</span><span class="p">:</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">o</span><span class="p">:</span> <span class="k">break</span> <span class="c1"># (or continue)</span> </pre></div> </div> <p>CPython will emit a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> in version 3.14, and we leave it open whether, and when, this will become a <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code>. However, we specify here that a <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> is permitted by the language spec, so that other Python implementations can choose to implement that.</p> <p>The CPython implementation will emit the <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> during <code class="docutils literal notranslate"><span class="pre">AST</span></code> construction, to ensure that the warning will show up during static anlaysis and compilation, but not during execution of pre-compiled code. We expect that the warning will be seen by a project maintainer (when they run static analysis, or CI which does not have precompiled files). However, end users of a project will only see a warning if they skip precompilation at installation time, check installation time warnings, or run static analysis over their dependencies.</p> </section> <section id="backwards-compatibility"> <h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2> <p>For backwards compatibility reasons, we are proposing that CPython emit only a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code>, with no concrete plan to upgrade that to an error. Code running with <code class="docutils literal notranslate"><span class="pre">-We</span></code> may stop working once this is introduced.</p> </section> <section id="security-implications"> <h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2> <p>The warning/error will help programmers avoid some hard to find bugs, so will have a security benefit. We are not aware of security issues related to raising a new <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> or <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code>.</p> </section> <section id="how-to-teach-this"> <h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2> <p>The change will be documented in the language spec and in the What’s New documentation. The <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> will alert users that their code needs to change. The <a class="reference external" href="#appendix">empirical evidence</a> shows that the changes necessary are typically quite straightforward.</p> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2> <section id="emit-syntaxerror-in-cpython"> <h3><a class="toc-backref" href="#emit-syntaxerror-in-cpython" role="doc-backlink">Emit <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> in CPython</a></h3> <p><a class="pep reference internal" href="../pep-0601/" title="PEP 601 – Forbid return/break/continue breaking out of finally">PEP 601</a> proposed that CPython would emit <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> for a couple of releases and <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> afterwards. We are leaving it open whether, and when, this will become a <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> in CPython, because we believe that a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> would provide most of the benefit with less risk.</p> </section> <section id="change-semantics"> <h3><a class="toc-backref" href="#change-semantics" role="doc-backlink">Change Semantics</a></h3> <p>It <a class="reference external" href="https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/32">was suggested</a> to change the semantics of control flow instructions in <code class="docutils literal notranslate"><span class="pre">finally</span></code> such that an in-flight exception takes precedence over them. In other words, a <code class="docutils literal notranslate"><span class="pre">return</span></code>, <code class="docutils literal notranslate"><span class="pre">break</span></code> or <code class="docutils literal notranslate"><span class="pre">continue</span></code> would be permitted, and would exit the <code class="docutils literal notranslate"><span class="pre">finally</span></code> block, but the exception would still be raised.</p> <p>This was rejected for two reasons. First, it would change the semantics of working code in a way that can be hard to debug: a <code class="docutils literal notranslate"><span class="pre">finally</span></code> that was written with the intention of swallowing all exceptions (correctly using the documented semantics) would now allow the exception to propagate on. This may happen only in rare edge cases at runtime, and is not guaranteed to be detected in testing. Even if the code is wrong, and has an exception swallowing bug, it could be hard for users to understand why a program started raising exceptions in 3.14, while it did not in 3.13. In contrast, a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> is likely to be seen during testing, it would point to the precise location of the problem in the code, and it would not prevent the program from running.</p> <p>The second objection was about the proposed semantics. The motivation for allowing control flow statements is not that this would be useful, but rather the desire for orthogonality of features (which, as we mentioned in the introduction, is already violated in the case of <code class="docutils literal notranslate"><span class="pre">except*</span></code> clauses). However, the proposed semantics are complicated because they suggest that <code class="docutils literal notranslate"><span class="pre">return</span></code>, <code class="docutils literal notranslate"><span class="pre">break</span></code> and <code class="docutils literal notranslate"><span class="pre">continue</span></code> behave as they normally do when <code class="docutils literal notranslate"><span class="pre">finally</span></code> executes without an in-flight exception, but turn into something like a bare <code class="docutils literal notranslate"><span class="pre">raise</span></code> when there is one. It is hard to claim that the features are orthogonal if the presence of one changes the semantics of the other.</p> </section> </section> <section id="appendix"> <h2><a class="toc-backref" href="#appendix" role="doc-backlink">Appendix</a></h2> <section id="return-in-finally-considered-harmful"> <h3><a class="toc-backref" href="#return-in-finally-considered-harmful" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">return</span></code> in <code class="docutils literal notranslate"><span class="pre">finally</span></code> considered harmful</a></h3> <p>Below is an abridged version of a <a class="reference external" href="https://github.com/iritkatriel/finally/commits/main/README.md">research report</a> by Irit Katriel, which was posted on 9 Nov 2024. It describes an investigation into usage of <code class="docutils literal notranslate"><span class="pre">return</span></code>, <code class="docutils literal notranslate"><span class="pre">break</span></code> and <code class="docutils literal notranslate"><span class="pre">continue</span></code> in a <code class="docutils literal notranslate"><span class="pre">finally</span></code> clause in real world code, addressing the questions: Are people using it? How often are they using it incorrectly? How much churn would the proposed change create?</p> <section id="method"> <h4><a class="toc-backref" href="#method" role="doc-backlink">Method</a></h4> <p>The analysis is based on the 8,000 most popular PyPI packages, in terms of number of downloads in the last 30 days. They were downloaded on the 17th-18th of October, using <a class="reference external" href="https://github.com/faster-cpython/tools/blob/main/scripts/download_packages.py">a script</a> written by Guido van Rossum, which in turn relies on Hugo van Kemenade’s <a class="reference external" href="https://hugovk.github.io/top-pypi-packages/">tool</a> that creates a list of the most popular packages.</p> <p>Once downloaded, a <a class="reference external" href="https://github.com/iritkatriel/finally/blob/main/scripts/ast_analysis.py">second script</a> was used to construct an AST for each file, and traverse it to identify <code class="docutils literal notranslate"><span class="pre">break</span></code>, <code class="docutils literal notranslate"><span class="pre">continue</span></code> and <code class="docutils literal notranslate"><span class="pre">return</span></code> statements which are directly inside a <code class="docutils literal notranslate"><span class="pre">finally</span></code> block.</p> <p>I then found the current source code for each occurrence, and categorized it. For cases where the code seems incorrect, I created an issue in the project’s bug tracker. The responses to these issues are also part of the data collected in this investigation.</p> </section> <section id="results"> <h4><a class="toc-backref" href="#results" role="doc-backlink">Results</a></h4> <p>I decided not to include a list of the incorrect usages, out of concern that it would make this report look like a shaming exercise. Instead I will describe the results in general terms, but will mention that some of the problems I found appear in very popular libraries, including a cloud security application. For those so inclined, it should not be hard to replicate my analysis, as I provided links to the scripts I used in the Method section.</p> <p>The projects examined contained a total of 120,964,221 lines of Python code, and among them the script found 203 instances of control flow instructions in a <code class="docutils literal notranslate"><span class="pre">finally</span></code> block. Most were <code class="docutils literal notranslate"><span class="pre">return</span></code>, a handful were <code class="docutils literal notranslate"><span class="pre">break</span></code>, and none were <code class="docutils literal notranslate"><span class="pre">continue</span></code>. Of these:</p> <ul class="simple"> <li>46 are correct, and appear in tests that target this pattern as a feature (e.g., tests for linters that detect it).</li> <li>8 seem like they could be correct - either intentionally swallowing exceptions or appearing where an active exception cannot occur. Despite being correct, it is not hard to rewrite them to avoid the bad pattern, and it would make the code clearer: deliberately swallowing exceptions can be more explicitly done with <code class="docutils literal notranslate"><span class="pre">except</span> <span class="pre">BaseException:</span></code>, and <code class="docutils literal notranslate"><span class="pre">return</span></code> which doesn’t swallow exceptions can be moved after the <code class="docutils literal notranslate"><span class="pre">finally</span></code> block.</li> <li>149 were clearly incorrect, and can lead to unintended swallowing of exceptions. These are analyzed in the next section.</li> </ul> <p><strong>The Error Cases</strong></p> <p>Many of the error cases followed this pattern:</p> <div class="bad highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span> <span class="o">...</span> <span class="k">except</span> <span class="n">SomeSpecificError</span><span class="p">:</span> <span class="o">...</span> <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span> <span class="n">logger</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="k">finally</span><span class="p">:</span> <span class="k">return</span> <span class="n">some_value</span> </pre></div> </div> <p>Code like this is obviously incorrect because it deliberately logs and swallows <code class="docutils literal notranslate"><span class="pre">Exception</span></code> subclasses, while silently swallowing <code class="docutils literal notranslate"><span class="pre">BaseExceptions</span></code>. The intention here is either to allow <code class="docutils literal notranslate"><span class="pre">BaseExceptions</span></code> to propagate on, or (if the author is unaware of the <code class="docutils literal notranslate"><span class="pre">BaseException</span></code> issue), to log and swallow all exceptions. However, even if the <code class="docutils literal notranslate"><span class="pre">except</span> <span class="pre">Exception</span></code> was changed to <code class="docutils literal notranslate"><span class="pre">except</span> <span class="pre">BaseException</span></code>, this code would still have the problem that the <code class="docutils literal notranslate"><span class="pre">finally</span></code> block swallows all exceptions raised from within the <code class="docutils literal notranslate"><span class="pre">except</span></code> block, and this is probably not the intention (if it is, that can be made explicit with another <code class="docutils literal notranslate"><span class="pre">try</span></code>-<code class="docutils literal notranslate"><span class="pre">except</span> <span class="pre">BaseException</span></code>).</p> <p>Another variation on the issue found in real code looks like this:</p> <div class="bad highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="k">try</span><span class="p">:</span> <span class="o">...</span> <span class="k">except</span><span class="p">:</span> <span class="k">return</span> <span class="bp">NotImplemented</span> <span class="k">finally</span><span class="p">:</span> <span class="k">return</span> <span class="n">some_value</span> </pre></div> </div> <p>Here the intention seems to be to return <code class="docutils literal notranslate"><span class="pre">NotImplemented</span></code> when an exception is raised, but the <code class="docutils literal notranslate"><span class="pre">return</span></code> in the <code class="docutils literal notranslate"><span class="pre">finally</span></code> block would override the one in the <code class="docutils literal notranslate"><span class="pre">except</span></code> block.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>Following the <a class="reference external" href="https://discuss.python.org/t/an-analysis-of-return-in-finally-in-the-wild/70633/15">discussion</a>, I repeated the analysis on a random selection of PyPI packages (to analyze code written by <em>average</em> programmers). The sample contained in total 77,398,892 lines of code with 316 instances of <code class="docutils literal notranslate"><span class="pre">return</span></code>/<code class="docutils literal notranslate"><span class="pre">break</span></code>/<code class="docutils literal notranslate"><span class="pre">continue</span></code> in <code class="docutils literal notranslate"><span class="pre">finally</span></code>. So about 4 instances per million lines of code.</p> </div> <p><strong>Author reactions</strong></p> <p>Of the 149 incorrect instances of <code class="docutils literal notranslate"><span class="pre">return</span></code> or <code class="docutils literal notranslate"><span class="pre">break</span></code> in a <code class="docutils literal notranslate"><span class="pre">finally</span></code> clause, 27 were out of date, in the sense that they do not appear in the main/master branch of the library, as the code has been deleted or fixed by now. The remaining 122 are in 73 different packages, and I created an issue in each one to alert the authors to the problems. Within two weeks, 40 of the 73 issues received a reaction from the code maintainers:</p> <ul class="simple"> <li>15 issues had a PR opened to fix the problem.</li> <li>20 received reactions acknowledging the problem as one worth looking into.</li> <li>3 replied that the code is no longer maintained so this won’t be fixed.</li> <li>2 closed the issue as “works as intended”, one said that they intend to swallow all exceptions, but the other seemed unaware of the distinction between <code class="docutils literal notranslate"><span class="pre">Exception</span></code> and <code class="docutils literal notranslate"><span class="pre">BaseException</span></code>.</li> </ul> <p>One issue was linked to a pre-existing open issue about non-responsiveness to Ctrl-C, conjecturing a connection.</p> <p>Two of the issue were labelled as “good first issue”.</p> <p><strong>The correct usages</strong></p> <p>The 8 cases where the feature appears to be used correctly (in non-test code) also deserve attention. These represent the “churn” that would be caused by blocking the feature, because this is where working code will need to change. I did not contact the authors in these cases, so we need to assess the difficulty of making these changes ourselves. It is shown in <a class="reference external" href="https://github.com/iritkatriel/finally/commits/main/README.md">the full report</a>, that the change required in each case is small.</p> </section> <section id="discussion"> <h4><a class="toc-backref" href="#discussion" role="doc-backlink">Discussion</a></h4> <p>The first thing to note is that <code class="docutils literal notranslate"><span class="pre">return</span></code>/<code class="docutils literal notranslate"><span class="pre">break</span></code>/<code class="docutils literal notranslate"><span class="pre">continue</span></code> in a <code class="docutils literal notranslate"><span class="pre">finally</span></code> block is not something we see often: 203 instance in over 120 million lines of code. This is, possibly, thanks to the linters that warn about this.</p> <p>The second observation is that most of the usages were incorrect: 73% in our sample (149 of 203).</p> <p>Finally, the author responses were overwhelmingly positive. Of the 40 responses received within two weeks, 35 acknowledged the issue, 15 of which also created a PR to fix it. Only two thought that the code is fine as it is, and three stated that the code is no longer maintained so they will not look into it.</p> <p>The 8 instances where the code seems to work as intended, are not hard to rewrite.</p> </section> </section> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0765.rst">https://github.com/python/peps/blob/main/peps/pep-0765.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0765.rst">2025-03-20 10:09:14 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a></li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#security-implications">Security Implications</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#emit-syntaxerror-in-cpython">Emit <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> in CPython</a></li> <li><a class="reference internal" href="#change-semantics">Change Semantics</a></li> </ul> </li> <li><a class="reference internal" href="#appendix">Appendix</a><ul> <li><a class="reference internal" href="#return-in-finally-considered-harmful"><code class="docutils literal notranslate"><span class="pre">return</span></code> in <code class="docutils literal notranslate"><span class="pre">finally</span></code> considered harmful</a><ul> <li><a class="reference internal" href="#method">Method</a></li> <li><a class="reference internal" href="#results">Results</a></li> <li><a class="reference internal" href="#discussion">Discussion</a></li> </ul> </li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0765.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>