Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙️ Pengaturan Proxy Target

Skip to content

[ExceptionGroup] Inconsistent raise inside except* #103590

Closed

Description

@andriilahuta

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

Activity

  1. mdboom commented on Apr 17, 2023

    @mdboom
    Contributor
  2. sunmy2019 commented on Apr 17, 2023

    @sunmy2019
    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
          +------------------------------------
    >>>
  3. andriilahuta commented on Apr 17, 2023

    @andriilahuta
    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?

  4. carljm commented on Apr 17, 2023

    @carljm
    Member

    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.

  5. added a commit that references this issue on Apr 21, 2023
  6. iritkatriel commented on Apr 21, 2023

    @iritkatriel
    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.

  7. added 4 commits that reference this issue on Apr 27, 2023
  8. added a commit that references this issue on Jul 4, 2023
  9. added a commit that references this issue on Sep 4, 2023
  10. added a commit that references this issue on Dec 1, 2023
  11. added a commit that references this issue on May 1, 2024
  12. added a commit that references this issue on Aug 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions