# PEP 765: Disallow return/break/continue that exit a finally block

**URL:** <https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348>\
**Category:** PEPs\
**Created:** [November 16, 2024, 10:28am UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348 "2024-11-16T10:28:23Z")\
**Posts on this page:** 20\
**Page:** 6

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [November 23, 2024, 6:28am UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/102 "2024-11-23T06:28:50Z")

</div>

I abandoned this thread for a while, but I will point out that if an inner “finally” is exited this way, an outer “finally” still runs!

I still have mixed feelings and wish the next SC good luck figuring it out. I honestly don’t know what I would have done when I was BDFL.

---

<div class="post-metadata">

**Author:** ![pitrou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pitrou/32/28_2.png) [@pitrou](https://discuss.python.org/u/pitrou)\
**Post date:** [November 23, 2024, 9:30am UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/103 "2024-11-23T09:30:35Z")

</div>

> [@steve.dower](#):
>
> I may have missed it in the earlier discussion (but hopefully I didn’t miss it in the PEP text) - why are we forbidding the construct rather than just making exceptions re-raise on exit from `finally`, regardless of how we leave the block?

So the `return`/`break`/`continue` statement in that `finally` block would be silently ignored?

You may argue that this is the “correct” behavior, but that doesn’t seem obvious to me. The problem is _we don’t know what the original developer intended_. Python’s usual stance on this is to “avoid the temptation to guess”.

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [November 23, 2024, 11:44am UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/104 "2024-11-23T11:44:16Z")

</div>

> [@pitrou](#):
>
> So the `return`/`break`/`continue` statement in that `finally` block would be silently ignored?

Only in that it doesn’t exit the function (which is already exiting due to an exception) or repeat the loop (which is already broken due to an exception).

Any later statements in the `finally` block would not be executed. And any outer `finally` blocks would still be executed (thanks Guido!).

Worth noting that raising an exception from a `finally` block when an exception is already being raised will _chain_ the exception, which means we already acknowledge that the original exception is still relevant. Applying that logic to all ways to early-exit the `finally` block seems pretty consistent to me (though making that change is nearly as hard as making the proposed one, hence my preference for status quo).

---

<div class="post-metadata">

**Author:** ![Liz](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/liz/32/15021_2.png) [@Liz](https://discuss.python.org/u/Liz)\
**Post date:** [November 23, 2024, 6:42pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/105 "2024-11-23T18:42:32Z")

</div>

I don’t like this change. Either make it an error or don’t make it a warning. This halfway-inbetween stance is targeting a small number of users and telling them they are wrong, but without the conviction to actually make it an error, and instead doing it in a way that turns it into “lets annoy your users to shame you into changing it”

For what it’s worth, I don’t think this should be an error or a warning, and this would make some code I maintain much more convoluted where finally is intentionally used to silence errors and return a value, and there is exception handling involved, requiring code duplication in branches to actually get the desired ordering.

---

<div class="post-metadata">

**Author:** ![BrenBarn](https://avatars.discourse-cdn.com/v4/letter/b/74df32/32.png) [@BrenBarn](https://discuss.python.org/u/BrenBarn)\
**Post date:** [November 23, 2024, 8:23pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/106 "2024-11-23T20:23:21Z")

</div>

> [@iritkatriel](#):
>
> The point here is that we don’t think this should be valid python code, it’s not useful and it causes people to write bugs. We want to remove this from the language but without breaking stuff.

I don’t see how that combination of desiderata is achievable. The only way to _remove_ it is to break stuff; if you raise a warning you don’t break stuff but also don’t remove it.

> [@iritkatriel](#):
>
> Linters are not in a position to make such a declaration. Should there be no way for python to do that?

Regardless of “should”, I don’t see how there _can_ be a way. 🙂

> [@iritkatriel](#):
>
> All the possibly-correct cases were easy to rewrite without the problem feature.

This is something I’ve seen in other discussions about backwards compatibility, and I think it kind of misses the point. In my view, the important inflection point in terms of backwards compatibility is between “this code can still run with zero changes” and “this code can’t run without changes”. It doesn’t matter whether code that gets broken by a change is “easy to rewrite”; the point of backwards compatibility is to ensure that old code can run _exactly as is_.

As I mentioned in an earlier post, it might sometimes be worthwhile to break backwards compatibility, but I don’t see it as useful to try to break it “just a little bit” to fix minor things like this.

So if we don’t want to break things, we can’t change this behavior. If we can’t change the behavior, I don’t think it’s a great idea to just add a warning. There are endless dubious coding practices we could warn about, but I’d rather not go down that slippery slope. It would be better to encourage linters to add a check for this, and perhaps to add a warning note to the docs.

---

<div class="post-metadata">

**Author:** ![ncoghlan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ncoghlan/32/14266_2.png) [@ncoghlan](https://discuss.python.org/u/ncoghlan)\
**Post date:** [November 25, 2024, 9:13pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/107 "2024-11-25T21:13:34Z")

</div>

> [@MegaIng](#):
>
> The code _probably_ doesn’t do what the user wants, but we are never going to make it a full error.

The proposal moves the construct into the “may not work on all implementations” category. Allowing _new_ Python implementations to skip implementing this feature was the primary motivation in PEP 601 (the MicroPython devs didn’t want to support it), and that aspect is still part of the motivation for this PEP.

Essentially, we want to demote this behaviour from “Python language feature that all compliant implementations offer” to “CPython implementation detail that other implementations may or may not support”. That can be done _without_ adding even the syntax warning to CPython, but if the demotion happens, a syntax warning makes the portability issue explicit.

---

<div class="post-metadata">

**Author:** ![JeffGlass](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jeffglass/32/17293_2.png) [@JeffGlass](https://discuss.python.org/u/JeffGlass)\
**Post date:** [November 26, 2024, 7:47pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/108 "2024-11-26T19:47:21Z")

</div>

I don’t know if this is the best place to chip in specific feedback on wording in the PEP, since the discussion is still fairly high-level. But in reading the PEP for the first time, just one small clarity thing.

In the _Specifications_ section, it’s not super clear _at first glance_ which examples have new behavior and which don’t, just based on the verbs “include”/“exclude”. Perhaps the headings could change to:

“This includes the following examples” → "These examples may emit a `SyntaxWarning` or `SyntaxError`

and

“But excludes these” → “These examples would not emit the warning or error”

---

<div class="post-metadata">

**Author:** ![rrolls](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rrolls/32/10842_2.png) [@rrolls](https://discuss.python.org/u/rrolls)\
**Post date:** [November 29, 2024, 10:43pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/109 "2024-11-29T22:43:02Z")

</div>

I’ve been mulling this proposal over for a few days now since first noticing this thread.

Personally I have been bitten by this gotcha many times, and IMO being able to exit a finally block in this way should never have been allowed in the first place.

Although I don’t like the idea of adding a `SyntaxWarning` for something without a plan to eventually make it a `SyntaxError` - it doesn’t feel like the right way to do things - I suppose it still brings the benefit of being alerted to the problem, and doesn’t really have a downside in practice, only in philosophy. So I guess I can say I’m -0 on this PEP.

What I’d love to see is for PEP 601, which would have made this a `SyntaxError`, to be revived, reconsidered, and accepted. I suppose if PEP 765 gets accepted, and that causes PEP 601 to become acceptable in future, then it will have been worth it.

Linters and static analysis tools should definitely gain a rule to forbid this facility if they don’t already have one, even if it’s optional and off by default; at least people could then opt in to forbidding it from their own code.

---

<div class="post-metadata">

**Author:** ![ncoghlan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ncoghlan/32/14266_2.png) [@ncoghlan](https://discuss.python.org/u/ncoghlan)\
**Post date:** [December 8, 2024, 6:16am UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/110 "2024-12-08T06:16:39Z")

</div>

> [@rrolls](#):
>
> Linters and static analysis tools should definitely gain a rule to forbid this facility if they don’t already have one, even if it’s optional and off by default; at least people could then opt in to forbidding it from their own code.

Forbidding it in your own code doesn’t help you if it’s one of your dependencies that is eating the exception. All linter based mitigations for this have that problem: they don’t tell you if one of your dependencies is at risk of making exceptions unexpectedly vanish.

By contrast, if the compiler is doing it, then app developers get notified no matter if it’s their own code or code in one of their dependencies which has the dubious construct.

I’m thoroughly sympathetic to folks favouring eventual escalation to a full `SyntaxError` (I was the core dev sponsor for PEP 601), but the following numbers from the project author responses in Irit’s report concern me:

- “3 replied that the code is no longer maintained so this won’t be fixed.”
- 33 did not respond (at least in the time frame covered by the report)

When CPython is only emitting `SyntaxWarning`, app developers have options for attempting to suppress that warning (this can be done programmatically for implicit runtime compilation, but can be a bit trickier for installation time pre-compilation).

If CPython escalates to a full `SyntaxError`, the problem becomes _much_ harder to work around without outright forking the affected dependency.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [February 6, 2025, 11:00pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/111 "2025-02-06T23:00:15Z")

</div>

Hello @iritkatriel and @ncoghlan !

The SC discussed this PEP today and we’re happy to say that we accept it unanimously. The PEP was well-written and obviously persuasive. We were particularly swayed by the consistency with `except*`.

Congratulations!

---

<div class="post-metadata">

**Author:** ![iritkatriel](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/iritkatriel/32/2615_2.png) [@iritkatriel](https://discuss.python.org/u/iritkatriel)\
**Post date:** [February 7, 2025, 12:21am UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/112 "2025-02-07T00:21:46Z")

</div>

Thank you @barry and the rest of the SC. We will break it with care.

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [August 10, 2025, 5:01pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/113 "2025-08-10T17:01:40Z")

</div>

> [@pf\_moore](#):
>
> And what’s worse, the only way of fixing it is for the library developer to change the code (and release a new version). Because as far as I can see, the runtime warning filter doesn’t work for syntax warnings:

> [@pf\_moore](#):
>
> While I appreciate that this problem may be very rare, a syntax warning seems like it will cause _at least_ as many problems as we’ve had with other forms of deprecation warnings. IMO, if we think this needs to be prohibited, we should stand by our decision, and simply make it an error. If there’s too much risk of disruption from doing that, I think there’s _also_ too much risk of disruption using a syntax warning.

This has caused [CI failures for someone testing 3.14rc1](https://github.com/Rapptz/discord.py/discussions/10240#discussioncomment-14055608)

I’m not sure what I should recommend to this user other than “now you need to suppress specific warnings in your CI at a level above python”; It also feels wrong to change code that is working as intended because of what amounts to a linter warning with no specific plan to actually make it an error.

---

<div class="post-metadata">

**Author:** ![cdce8p](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cdce8p/32/3575_2.png) [@cdce8p](https://discuss.python.org/u/cdce8p)\
**Post date:** [August 10, 2025, 6:46pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/115 "2025-08-10T18:46:23Z")

</div>

A while ago I opened [Better module info for SyntaxWarnings during AST parsing · Issue #135801 · python/cpython · GitHub](https://github.com/python/cpython/issues/135801) to track the issue. There is even an open PR for it, though I suspect it sadly won’t make 3.14 at this point.

> <https://github.com/python/cpython/pull/135829>
>
> This commit addresses the inaccurate module information provided for SyntaxWarni…ngs during AST parsing, specifically reported in issue #135801.
> 
> Previously, \`\_PyErr\_EmitSyntaxWarning\` (used by the compiler) relied on \`\_PyErr\_WarnExplicitObjectWithContext\` to infer the module name from the call stack. During compilation, this often resulted in misleading module names like \`sys\` or \`importlib.\_bootstrap\`, making it difficult to filter warnings effectively (e.g., using \`-W error::SyntaxWarning:test\`).
> 
> To resolve this, a new internal function \`\_PyErr\_EmitSyntaxWarningFromCompiler\` was introduced in \`Python/errors.c\`. This function explicitly calls \`PyErr\_WarnExplicitObject\` with a \`NULL\` module argument, forcing the warning system to derive the module name from the filename provided by the compiler.
> 
> The \`normalize\_module\` function in \`Python/\_warnings.c\` was also refined. Instead of simple string manipulation or problematic pure C path parsing, it now leverages Python's \`os.path\` module (via Python C API calls like \`basename\`, \`splitext\`, and \`dirname\`). This ensures robust and accurate extraction of module names from arbitrary file paths, including correct handling of \`\_\_init\_\_.py\` files, thereby providing correct module information for compiler-generated \`SyntaxWarning\` messages.
> 
> This approach ensures cross-platform compatibility and reliability by utilizing Python's battle-tested path handling capabilities, directly addressing the core issue reported.
> 
> 
> \* Issue: gh-135801

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [August 10, 2025, 7:03pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/116 "2025-08-10T19:03:05Z")

</div>

Yeah, I’m genuinely unsure what I should recommend. This is a user downstream from the library where the code exists.

The standard way to fix it without a code change of stable library code would be a warning filter provided by the library for this with us assuming responsibility for keeping aware of the situation and any later decision to promote this to an error, or suggest the user could do so in CI.

If the only answer that reasonably works on the library side is for the library to change the working code, this is effectively just as bad as a breaking change without standard notice, just using the fact that software is interconnected and the choice of where this is surfaced to break it by pressuring the social contract rather than it actually breaking the code that is running.

---

<div class="post-metadata">

**Author:** ![ncoghlan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ncoghlan/32/14266_2.png) [@ncoghlan](https://discuss.python.org/u/ncoghlan)\
**Post date:** [August 11, 2025, 5:22pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/117 "2025-08-11T17:22:16Z")

</div>

The PR you posted at [Upstream change in Python by mikeshardmind · Pull Request #10257 · Rapptz/discord.py · GitHub](https://github.com/Rapptz/discord.py/pull/10257/) _is_ the recommended change for robust 3.14 compatibility (making it explicit that all exceptions are being suppressed in that code, not just regular exceptions).

That was the main decision the Steering Council had to make regarding the PEP: whether inadvertently suppressing all exceptions was a frequent enough error to justify requiring folks that were intentionally suppressing all exceptions to tweak their code to make that intent clearer to readers.

For downstream consumers of affected libraries, ensuring their dependencies are precompiled at installation time should be sufficient to avoid the warning in CI (which `pip` does by default, but `uv` doesn’t). The warning is emitted during AST traversal, so imports from already compiled modules won’t say anything:

```python
$ cat > pep765.py
def f():
    try:
        1/0
    finally:
        return

$ python3.14 -c "import pep765"
/home/acoghlan/pep765.py:5: SyntaxWarning: 'return' in a 'finally' block
  return
$ python3.14 -c "import pep765"
$

```

The first run compiled the module, so the second run didn’t complain.

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [August 11, 2025, 6:07pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/118 "2025-08-11T18:07:31Z")

</div>

> [@ncoghlan](#):
>
> For downstream consumers of affected libraries, ensuring their dependencies are precompiled at installation time should be sufficient to avoid the warning in CI (which `pip` does by default, but `uv` doesn’t). The warning is emitted during AST traversal, so imports from already compiled modules won’t say anything:

Okay, up above, one of the things people asked about was if downstream libraries would be affected, and we were told they would not because of precompilation. This seems to have been only accurate regarding pip. Perhaps the intent was unclear with this, or maybe there was an assumption that installers precompile unless told not to (which is not the case) impacting how this was conveyed, but the user effected has not explicitly skipped this:

> [@ncoghlan](#):
>
> With those triggers, maintainers of a project will almost inevitably see the warning (even if they don’t run static analysis, CI often doesn’t have files precompiled), but the only way for an end user of an affected project to see it is if they skip precompilation at installation time

* * *

> [@ncoghlan](#):
>
> The PR you posted at [Upstream change in Python by mikeshardmind · Pull Request #10257 · Rapptz/discord.py · GitHub](https://github.com/Rapptz/discord.py/pull/10257/) _is_ the recommended change for robust 3.14 compatibility

Which because this is causing downstream CI to error, has basically become a breaking change,

---

<div class="post-metadata">

**Author:** ![ncoghlan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ncoghlan/32/14266_2.png) [@ncoghlan](https://discuss.python.org/u/ncoghlan)\
**Post date:** [August 11, 2025, 6:42pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/119 "2025-08-11T18:42:34Z")

</div>

> [@mikeshardmind](#):
>
> Which because this is causing downstream CI to error, has basically become a breaking change,

Any time we add a new `DeprecationWarning` is frequently a breaking change for CI systems, and `SyntaxWarning` isn’t any different in that regard.

The PEP specifically called this out in the Backwards Compatibility section: “Code running with `-We` may stop working once this is introduced.”

“Zero breaking changes” isn’t a design goal for feature releases though, it’s instead more:

- any breaking changes should be justified; and
- any breaking changes should preferably have a way to deal with them that’s also valid in older still supported releases (rather than users having to rely on explicit version checks)

The justification in this case was the 3:1 ratio found in public code between incorrect latent defects and correct usage of the feature (with only small updates needed in the latter cases to avoid relying on the dubious behaviour).

PEP 765 has a few approaches in the latter list:

- avoid the construct the generates the warning
- add an explicit warning filter to suppress the warning
- precompile the code so the warning is emitted at a point where warnings aren’t reported as an error

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [August 11, 2025, 6:54pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/120 "2025-08-11T18:54:41Z")

</div>

> [@ncoghlan](#):
>
> “Zero breaking changes” isn’t a design goal for feature releases though, it’s instead more:
> 
> - any breaking changes should be justified; and
> - any breaking changes should preferably have a way to deal with them that’s also valid in older still supported releases (rather than users having to rely on explicit version checks)

I understand this, but when people were asking about if the impact might be too large, you reassured them that unless a user opts out of precompilation, it wouldn’t be visible downstream. This isn’t actually accurate, but I took it at face value, and seemingly so did others in the conversation. It happens to be accurate for pip, because currently anyway, that’s pip’s default behavior.

---

<div class="post-metadata">

**Author:** ![ncoghlan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ncoghlan/32/14266_2.png) [@ncoghlan](https://discuss.python.org/u/ncoghlan)\
**Post date:** [August 11, 2025, 7:21pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/121 "2025-08-11T19:21:24Z")

</div>

> [@mikeshardmind](#):
>
> when people were asking about if the impact might be too large, you reassured them that unless a user opts out of precompilation, it wouldn’t be visible downstream

Fair. I don’t actually know what the level of adoption of `uv` is (aside from “higher than it was a year ago”), but there was certainly an assumption on my part at the time that the intersection of “uses `uv`”, “hasn’t turned precompilation back on” and “has a dependency affected by this issue” would be pretty close to the null set.

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [August 11, 2025, 7:29pm UTC](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348/122 "2025-08-11T19:29:32Z")

</div>

I’ll go ahead and pass on the option to precompile to the downstream user. I _think_ there will be another release before 3.14 leaves rc status.

Sorry if some of this came across overly bitter, I realized when I came back to it that could be the case, I mostly just want to make sure that when impacts are considered, that we’re keeping in mind various things like pip not being the only resolver, and that defaults for others have to be checked as well.

I don’t think in the grand scheme of things this one is a big deal, but as a process thing, I wouldn’t want the same happening on a larger change.

[Previous page](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348.md?page=5)

[Next page](https://discuss.python.org/t/pep-765-disallow-return-break-continue-that-exit-a-finally-block/71348.md?page=7)
