Hi Paul,

On Sat, Mar 27, 2021 at 6:00 PM Paul Sokolovsky <pmiscml@gmail.com> wrote:

It definitely feels like a lot of effort went into devising and
polishing ExceptionGroup's and except*, thanks. But I'm not sure if you
gentlemen come up with the "ultimate" way to deal with multiple errors,

I've been mistaken for a man before, but no-one has ever confused me for gentle. I'll take that as a compliment.
 
which deserves being codified on the language level (vs be added as
pluggable means indeed).


Pluggable is not without its problems. I'm all in favor of you developing this idea and proposing an alternative.
As I said before, you just need to answer two questions:
1. show a limitation of our approach  (the contrived flat-set is not one - see below)
2. describe how a pluggable approach works for that case 


>   > > def chained(e, excs):
> > >     while e:
> > >         if isinstance(e, excs):
> > >             return e
> > >         e = e.__cause__  # Simplified, should consider __context__
> > > too
> > >
> > > try:
> > >     tempfile.TemporaryDirectory(...)
> > > except *chained(OSError) as e:
> > >     print(e) 
>
> What if the user code raised an OSError too, so now that exception
> group has more than one?

I don't see how that's different from PEP654's ExceptionGroup's case.
It's just in ExceptionGroup, there would be 2 (unrelated?) OSError's,
while using normal chaining rules, there's at least context info which
is preserved.

It's different because your "match" function returns a single exception (the first one that is of OSError type). Any further OSErrors will be reraised.  The PEP's except* knows how to match multiple exceptions of the relevant type.  
 

> I am interested to know (1) what limitations you see in the
> ExceptionGroup type we are proposing (in what sense is it
> special-purposed?

Just as a specific example, what if my application indeed needs
ExceptionGroup's (and not just more flexible matching of existing
exception chains), but I want it to be a bit simpler: a) to have a flat
structure of contained exceptions; b) want to actually emphasize the
users that they should not rely on the ordering, so want it to be a set.

My understanding is that ExceptionGroup is carefully designed to allow
subclassing it and override behavior, and the above should be possible
by subclassing.


Indeed:

    class PaulsExceptionGroup(ExceptionGroup):
        def __new__(cls, message, excs):
            for e in excs:
                if isinstance(e, BaseExceptionGroup):
                    raise TypeError("me is a flat exception group")
            return super().__new__(cls, message, excs)

        @property
        def exceptions(self):
            return set(super().exceptions)

        def derive(self, excs):
            return PaulsExceptionGroup(self.message, excs)


    eg1 = PaulsExceptionGroup("Paul's group", [ValueError(12), TypeError(42)])
    print(repr(eg1))
    print(eg1.exceptions)

    match, rest = eg1.split(ValueError)
    print(f'{match=!r}\n{rest=!r}')
    print(f'{match.exceptions=}\n{rest.exceptions=}')

Output:
    PaulsExceptionGroup("Paul's group", [ValueError(12), TypeError(42)])
    {TypeError(42), ValueError(12)}
    match=PaulsExceptionGroup("Paul's group", [ValueError(12)])
    rest=PaulsExceptionGroup("Paul's group", [TypeError(42)])
    match.exceptions={ValueError(12)}
    rest.exceptions={TypeError(42)}



 See - split() just works for you out of the box.


But usually, a simpler base class is subclassed to have
more advanced/complex behavior. So again, current ExceptionGroup feels
kind of specialized with its careful attention to hierarchy and
multi-story "3D" tracebacks.

So your concern with our design is that ExceptionGroup implements a generic split() that handles tracebacks and nested structure correctly, and you might not need that because maybe you don't nest or you don't care about tracebacks? 

If that is the case then I think you are confusing "generic" with "special-purpose". ExceptionGroup is generic, it works for general nested groups with arbitrary tracebacks and cause/context.  That's the opposite of special-purpose.

 Irit