|
| 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