[Subthread: handling individual errors]
Rather than iterator, I think we should add a visitor that calls a
function for each leaf exception,
I agree that we shouldn't add an `__iter__` method. But what if we added a separate method that returns an iterator? Then you could write ``` for e in eg.something(): print("caught", e) ``` This looks cleaner to me than having to write ``` def handler(e): print("caught", e) eg.something(handler) ``` (Does my bias against callback functions shine through? :-)
plus a utility that returns the traceback of a leaf exception (as a single list, copying frames from the tracebacks of ExceptionGroups
so that it's not destructive.)
Hm, what would the arguments for that utility be? Since ExceptionGroups can be nested, if you pass in the root EG and the leaf exception, it would require a full tree search to find the nodes that have all the needed tracebacks.
Maybe the API for the iterator-returning method should be that it returns an iterator of (exception, list-of-tracebacks) pairs? Or maybe there should be two iterator-returning methods, one that yields just exceptions and one that yields such pairs, so that if you don't need the tracebacks we don't have to construct them. (Separate methods so the static type doesn't depend on the value of a flag.) If in the end we were to go for something with a handler callback we could do a similar thing.
On Wed, Feb 24, 2021 at 1:27 AM Irit Katriel firstname.lastname@example.org wrote:
On Wed, Feb 24, 2021 at 4:55 AM Guido van Rossum email@example.com wrote:
However there may be an omission in the PEP -- what if we want to do
something special for each suberror? If we just iterate over `eg.errors` we would have to do something recursive for exceptions wrapped inside multiple nested groups. We could add a helper method to ExceptionGroup that iterates over suberrors, flattening nested groups. But we'd need to do something special to recover the tracebacks there (groups share the common part of the traceback). Or we could say "if you need the traceback you're going to have to write something recursive" -- like we have to do in traceback.py. But we might as well generalize whatever we're doing there. (Irit: I suspect there's a piece of API design we missed here.)
This is mentioned briefly where we talk about why ExceptionGroup is not iterable: https://www.python.org/dev/peps/pep-0654/#the-exceptiongroup-api We left it out pending a use case.
Rather than iterator, I think we should add a visitor that calls a function for each leaf exception, plus a utility that returns the traceback of a leaf exception (as a single list, copying frames from the tracebacks of ExceptionGroups so that it's not destructive.) This way the expensive traceback construction is happening explicitly when it is needed.
I think accessing one exception at a time to do something other than formatting it is more likely to be overused than actually needed. We did that in an earlier version of the PEP for the "filter OSErrors by type" example, which we did with iteration and now do with subgroup:
try: low_level_os_operation() except *OSerror as errors: raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None