X Tutup
The Wayback Machine - https://web.archive.org/web/20231023215736/https://github.com/python/cpython/issues/103590
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ExceptionGroup] Inconsistent raise inside except* #103590

Closed
andriilahuta opened this issue Apr 17, 2023 · 5 comments · Fixed by #103665
Closed

[ExceptionGroup] Inconsistent raise inside except* #103590

andriilahuta opened this issue Apr 17, 2023 · 5 comments · Fixed by #103665
Labels
type-bug An unexpected behavior, bug, or error

Comments

@andriilahuta
Copy link

andriilahuta commented Apr 17, 2023

Bug report

When raising inside except* block and the caught exception wasn't an ExceptionGroup originally, then raised exception doesn't get wrapped in ExceptionGroup:

try:
    try:
        raise TypeError(1)  # prints ValueError(3)
        raise ExceptionGroup('', [TypeError(2)])  # prints ExceptionGroup('', [ValueError(3)])
    except* TypeError:
        raise ValueError(3)
except BaseException as e:
    print(repr(e))

Your environment

  • CPython versions tested on: 3.11.3
  • Operating system and architecture: Windows 11 / Ubuntu 22.04

Linked PRs

@andriilahuta andriilahuta added the type-bug An unexpected behavior, bug, or error label Apr 17, 2023
@mdboom
Copy link
Contributor

mdboom commented Apr 17, 2023

Cc: @iritkatriel

@sunmy2019
Copy link
Member

This is fully documented behavior. https://peps.python.org/pep-0654/#raising-new-exceptions

Raising a new instance of a naked exception does not cause this exception to be wrapped by an exception group. Rather, the exception is raised as is, and if it needs to be combined with other propagated exceptions, it becomes a direct child of the new exception group created for that.

>>> try:
...     raise ExceptionGroup("eg", [ValueError('a')])
... except* ValueError:
...     raise KeyError('x')
...
  | ExceptionGroup:  (1 sub-exception)
  +-+---------------- 1 ----------------
    | Exception Group Traceback (most recent call last):
    |   File "<stdin>", line 2, in <module>
    | ExceptionGroup: eg (1 sub-exception)
    +-+---------------- 1 ----------------
      | ValueError: a
      +------------------------------------
    |
    | During handling of the above exception, another exception occurred:
    |
    | Traceback (most recent call last):
    |   File "<stdin>", line 4, in <module>
    | KeyError: 'x'
    +------------------------------------
>>>
>>> try:
...     raise ExceptionGroup("eg", [ValueError('a'), TypeError('b')])
... except* ValueError:
...     raise KeyError('x')
...
  | ExceptionGroup:  (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | Exception Group Traceback (most recent call last):
    |   File "<stdin>", line 2, in <module>
    | ExceptionGroup: eg (1 sub-exception)
    +-+---------------- 1 ----------------
      | ValueError: a
      +------------------------------------
    |
    | During handling of the above exception, another exception occurred:
    |
    | Traceback (most recent call last):
    |   File "<stdin>", line 4, in <module>
    | KeyError: 'x'
    +---------------- 2 ----------------
    | Exception Group Traceback (most recent call last):
    |   File "<stdin>", line 2, in <module>
    | ExceptionGroup: eg (1 sub-exception)
    +-+---------------- 1 ----------------
      | TypeError: b
      +------------------------------------
>>>

@andriilahuta
Copy link
Author

@sunmy2019 thanks for the docs pointer. It is a bit confusing behavior for me, since I can't always be sure what I'm actually raising. But I guess we can close this then?

@carljm
Copy link
Contributor

carljm commented Apr 17, 2023

I don't think that the issue in the OP here clearly matches any example shown in the PEP. The key factor in the PEP example:

>>> try:
...     raise ExceptionGroup("eg", [ValueError('a'), TypeError('b')])
... except* ValueError:
...     raise KeyError('x')

is that the raised exception group includes both a ValueError (will be caught by the except* ValueError) and a TypeError (is not caught and thus must be propagated.) It is the latter TypeError that means the newly raised KeyError "needs to be combined with other propagated exceptions" (the TypeError), and thus a new ExceptionGroup is created for that.

In the OP example:

try:
    raise ExceptionGroup('', [TypeError(2)])
except* TypeError:
    raise ValueError(3)

the original exception group contains only a TypeError, which is caught by except* TypeError. So there are no remaining uncaught exceptions that must propagate along with the newly raised ValueError. So according to the wording in the PEP:

Rather, the exception is raised as is, and if it needs to be combined with other propagated exceptions, it becomes a direct child of the new exception group created for that

it is surprising (to me -- not an expert in the ExceptionGroup feature by any means) that a new ExceptionGroup would be created containing just the newly-raised ValueError.

The documentation says about this case:

Any remaining exceptions that were not handled by any except* clause are re-raised at the end, combined into an exception group along with all exceptions that were raised from within except* clauses.

This wording is more ambiguous than the wording in the PEP, and leaves open the possibility that a new exception group should always be created, even if we are raising just a single exception and there were no "remaining exceptions" to propagate.

So it is possible this is intended behavior, but it doesn't seem at all clear. I think we need input from @iritkatriel here.

@iritkatriel
Copy link
Member

I agree that the wording of the PEP implies that wrapping in an exception group should not happen unless it is necessary, and it is not necessary when there is only one exception.

I don't remember why I implemented it this way (with unit tests asserting this behaviour, no less). I'm going to assume that it was a simple mistake and make a PR to fix it. Will ask @gvanrossum for a second option.

iritkatriel added a commit to iritkatriel/cpython that referenced this issue May 2, 2023
iritkatriel added a commit that referenced this issue May 3, 2023
meili-bors bot added a commit to meilisearch/meilisearch-python that referenced this issue Jul 4, 2023
793: Bump exceptiongroup from 1.1.1 to 1.1.2 r=alallema a=dependabot[bot]

Bumps [exceptiongroup](https://github.com/agronholm/exceptiongroup) from 1.1.1 to 1.1.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst">exceptiongroup's changelog</a>.</em></p>
<blockquote>
<h1>Version history</h1>
<p>This library adheres to <code>Semantic Versioning 2.0 &lt;http://semver.org/&gt;</code>_.</p>
<p><strong>1.1.2</strong></p>
<ul>
<li>Changed handling of exceptions in exception group handler callbacks to not wrap a
single exception in an exception group, as per
<code>CPython issue 103590 &lt;https://github.com/python/cpython/issues/103590&gt;</code>_</li>
</ul>
<p><strong>1.1.1</strong></p>
<ul>
<li>Worked around
<code>CPython issue [#98778](https://github.com/agronholm/exceptiongroup/issues/98778) &lt;https://github.com/python/cpython/issues/98778&gt;</code>_,
<code>urllib.error.HTTPError(..., fp=None)</code> raises <code>KeyError</code> on unknown attribute
access, on affected Python versions. (PR by Zac Hatfield-Dodds)</li>
</ul>
<p><strong>1.1.0</strong></p>
<ul>
<li>Backported upstream fix for <a href="https://redirect.github.com/agronholm/exceptiongroup/issues/99553">gh-99553</a> (custom subclasses of <code>BaseExceptionGroup</code> that
also inherit from <code>Exception</code> should not be able to wrap base exceptions)</li>
<li>Moved all initialization code to <code>__new__()</code> (thus matching Python 3.11 behavior)</li>
</ul>
<p><strong>1.0.4</strong></p>
<ul>
<li>Fixed regression introduced in v1.0.3 where the code computing the suggestions would
assume that both the <code>obj</code> attribute of <code>AttributeError</code> is always available, even
though this is only true from Python 3.10 onwards
(<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/43">#43</a>; PR by Carl Friedrich Bolz-Tereick)</li>
</ul>
<p><strong>1.0.3</strong></p>
<ul>
<li>Fixed monkey patching breaking suggestions (on a <code>NameError</code> or <code>AttributeError</code>)
on Python 3.10 (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/41">#41</a>; PR by Carl Friedrich Bolz-Tereick)</li>
</ul>
<p><strong>1.0.2</strong></p>
<ul>
<li>Updated type annotations to match the ones in <code>typeshed</code></li>
</ul>
<p><strong>1.0.1</strong></p>
<ul>
<li>Fixed formatted traceback missing exceptions beyond 2 nesting levels of
<code>__context__</code> or <code>__cause__</code></li>
</ul>
<p><strong>1.0.0</strong></p>
<ul>
<li>Fixed
<code>AttributeError: 'PatchedTracebackException' object has no attribute '__cause__'</code>
on Python 3.10 (only) when a traceback is printed from an exception where an exception</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/571b12187c0eee916ad224f670fb7739b94fddc7"><code>571b121</code></a> Added the release version</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/821d5ebaadfd0b5b16c785a942f69e89933217bf"><code>821d5eb</code></a> Changed handling of a single exception raised in exception group handlers to ...</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/8a104ebc3f0201b5af0787bc90d09522f43e1d4c"><code>8a104eb</code></a> Fixed pre-commit error</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/cad96f68f9ddbb8d347ebe9c26544e26f99584f7"><code>cad96f6</code></a> Added .ruff_cache/ to .gitignore</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/111f33842e2ed91ad795d29d6ca4a1d493f6684d"><code>111f338</code></a> Fixed coveralls reporting</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/c926b0a0c1d6a4755382fb0ff0cc929cc28904b8"><code>c926b0a</code></a> Switched from flake8 and isort to ruff</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/0d6338707a1ff6e0dfcf4ffc392609b1cd7c16cd"><code>0d63387</code></a> Added Python 3.12 to the testing matrix</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/9bfc03f402cca2d671a42d00dafb56f5c1e75847"><code>9bfc03f</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/63">#63</a>)</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/c604dd818b7dd3b8817594fce048a6f85870832a"><code>c604dd8</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/62">#62</a>)</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/516e53262eb4608266f6b314764d2be23900ce1c"><code>516e532</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/61">#61</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/agronholm/exceptiongroup/compare/1.1.1...1.1.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=exceptiongroup&package-manager=pip&previous-version=1.1.1&new-version=1.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bmwiedemann pushed a commit to bmwiedemann/openSUSE that referenced this issue Jul 6, 2023
https://build.opensuse.org/request/show/1096944
by user mcepl + favogt_factory
- Update to 1.1.2:
  - Changed handling of exceptions in exception group handler
    callbacks to not wrap a single exception in an exception
    group, as per CPython issue gh#python/cpython#103590.
- Add skip-test_catch_handler_raises-for-older-311.patch
  (gh#agronholm/exceptiongroup#64).
meili-bors bot added a commit to meilisearch/meilisearch-python that referenced this issue Sep 4, 2023
837: Bump exceptiongroup from 1.1.2 to 1.1.3 r=sanders41 a=dependabot[bot]

Bumps [exceptiongroup](https://github.com/agronholm/exceptiongroup) from 1.1.2 to 1.1.3.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst">exceptiongroup's changelog</a>.</em></p>
<blockquote>
<h1>Version history</h1>
<p>This library adheres to <code>Semantic Versioning 2.0 &lt;http://semver.org/&gt;</code>_.</p>
<p><strong>1.1.3</strong></p>
<ul>
<li><code>catch()</code> now raises a <code>TypeError</code> if passed an async exception handler instead of
just giving a <code>RuntimeWarning</code> about the coroutine never being awaited. (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/66">#66</a>, PR by
John Litborn)</li>
<li>Fixed plain <code>raise</code> statement in an exception handler callback to work like a
<code>raise</code> in an <code>except*</code> block</li>
<li>Fixed new exception group not being chained to the original exception when raising an
exception group from exceptions raised in handler callbacks</li>
<li>Fixed type annotations of the <code>derive()</code>, <code>subgroup()</code> and <code>split()</code> methods to
match the ones in typeshed</li>
</ul>
<p><strong>1.1.2</strong></p>
<ul>
<li>Changed handling of exceptions in exception group handler callbacks to not wrap a
single exception in an exception group, as per
<code>CPython issue 103590 &lt;https://github.com/python/cpython/issues/103590&gt;</code>_</li>
</ul>
<p><strong>1.1.1</strong></p>
<ul>
<li>Worked around
<code>CPython issue [#98778](https://github.com/agronholm/exceptiongroup/issues/98778) &lt;https://github.com/python/cpython/issues/98778&gt;</code>_,
<code>urllib.error.HTTPError(..., fp=None)</code> raises <code>KeyError</code> on unknown attribute
access, on affected Python versions. (PR by Zac Hatfield-Dodds)</li>
</ul>
<p><strong>1.1.0</strong></p>
<ul>
<li>Backported upstream fix for <a href="https://redirect.github.com/agronholm/exceptiongroup/issues/99553">gh-99553</a> (custom subclasses of <code>BaseExceptionGroup</code> that
also inherit from <code>Exception</code> should not be able to wrap base exceptions)</li>
<li>Moved all initialization code to <code>__new__()</code> (thus matching Python 3.11 behavior)</li>
</ul>
<p><strong>1.0.4</strong></p>
<ul>
<li>Fixed regression introduced in v1.0.3 where the code computing the suggestions would
assume that both the <code>obj</code> attribute of <code>AttributeError</code> is always available, even
though this is only true from Python 3.10 onwards
(<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/43">#43</a>; PR by Carl Friedrich Bolz-Tereick)</li>
</ul>
<p><strong>1.0.3</strong></p>
<ul>
<li>Fixed monkey patching breaking suggestions (on a <code>NameError</code> or <code>AttributeError</code>)
on Python 3.10 (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/41">#41</a>; PR by Carl Friedrich Bolz-Tereick)</li>
</ul>
<p><strong>1.0.2</strong></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/31d77ffefa298eb6ab73a97c8cde0c3d5a535983"><code>31d77ff</code></a> Added the release version</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/516ade166c0f33c6e5bcb0f57bebc58960d8dee8"><code>516ade1</code></a> Updated type annotations to match typeshed (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/77">#77</a>)</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/8b8791b662c0f62a574a09f305cd204dfb0a6a05"><code>8b8791b</code></a> Fixed bare <code>raise</code> and exception chaining when a handler raises an exception ...</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/0c94abe0f86fd134990d2a59ad84714e0d3f24d6"><code>0c94abe</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/75">#75</a>)</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/0878b8331a905f18b4d39b24b105b3e514ada65b"><code>0878b83</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/74">#74</a>)</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/fc578bcc6a05d9dbf129778c229f82b5b7bee8ea"><code>fc578bc</code></a> Switched to trusted publishing</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/4640be7afa4822e5803f4e09c5201d6669b0d2f8"><code>4640be7</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/73">#73</a>)</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/14bf3ed050af2998d0667aa59db306c46e774a64"><code>14bf3ed</code></a> Fixed erroneous TypeError in test_async_handler()</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/c97103955556d859493a2f4750fb9dddf8a9c9b3"><code>c971039</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/72">#72</a>)</li>
<li><a href="https://github.com/agronholm/exceptiongroup/commit/1d604fbe0f5f216bb7efa0ceb932745379e5a4f2"><code>1d604fb</code></a> Made catch() raise TypeError on async handler (<a href="https://redirect.github.com/agronholm/exceptiongroup/issues/69">#69</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/agronholm/exceptiongroup/compare/1.1.2...1.1.3">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=exceptiongroup&package-manager=pip&previous-version=1.1.2&new-version=1.1.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants
X Tutup