In earlier versions of the PEP ExceptionGroup was not immutable and split actually removed matching exceptions from it. It was also iterable so you could get a flat list of all the contained leaf exceptions. Then we changed it. ExceptionGroup is a tree of exceptions, and the internal nodes of the tree (which are ExceptionGroups) have metadata on them - context, cause, traceback. If you want the full traceback of a leaf exception you need to concatenate the tracebacks of all the exceptions on the path from the root to the leaf. If you flatten an ExceptionGroup and create a new list ExceptionGroup(list(eg)) you don't lose metadata (unless eg's tree has depth 1). Similarly, if you add an exception it needs to have matching metadata to the ExceptionGroup you are adding it to. It's too easy to get that wrong. split() and subgroup() take care to preserve the correct metadata on all the internal nodes, and if you just use them you only make safe operations. This is why I am hesitating to add iteration utilities to the API. Like we did, people will naturally try that first, and it's not the safest API. We actually have the OSErrors example in the PEP, just above https://www.python.org/dev/peps/pep-0654/#caught-exception-objects: try: low_level_os_operation() except *OSerror as errors: raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None On Sun, Feb 28, 2021 at 6:30 AM Guido van Rossum <guido@python.org> wrote:
We really don’t want users pushing non-exceptions into the list, nor do we want e.g. KeyboardInterrupt to be added to a (non-Base-) ExceptionGroup.
There’s a pattern for what you propose using the split() method and a lambda, or you can keep the exceptions in a list and re-wrap them at the end.
On Sat, Feb 27, 2021 at 20:41 Cameron Simpson <cs@cskk.id.au> wrote:
On 26Feb2021 02:44, Irit Katriel <iritkatriel@googlemail.com> wrote:
On Fri, Feb 26, 2021 at 2:00 AM Guido van Rossum <guido@python.org> wrote:
OT: Is ExceptionGroup *really* immutable in the current implementation? As long as the 'errors' field is a list, I think one could mutate the list directly.
It's not, but we were going to make it an immutable tuple.
Could you say why? Other than wanting to discourage mutation happy code getting out there?
The reason I ask is that the scenario which comes to my mind is something like:
except *OSError as e:
AIUI "e" is an ExceptionGroup containing only OSErrors. So, one common thing in my own code is this:
try: do something with a file except OSError as e: if e.errno == ENOENT: # file is missing, but that is ok # because we treat it like an empty file elif ... some other ok situation ... else: raise
My natural inclination with an ExceptionGroup would be to winnow the OSErrors I'm handed, and push the _unhandled_ errors back into the original ExceptionGroup. That way, after the various except* clauses, a nonempty ExceptionGroup would remain with the unhandled errors, and it might perhaps be reraised then.
Cheers, Cameron Simpson <cs@cskk.id.au>
-- --Guido (mobile)