Nathaniel Smith (in a conversation with Irit) wrote:
... suggested having each 'except Blah as exc' clause be executed once, receiving an ExceptionGroup containing all the Blah exceptions. Guido pointed out that this broke typing -- 'exc' would not longer have type 'Blah' -- and I was like... okay yeah that's a fatal flaw, never mind.
The fact that "Blah as exc" could return either a single Blah or a collection of Blahs would be annoying, but not fatal -- that seems well within the level of ambiguity typing can deal with.
never seriously raised the 'execute a single clause multiple times' option, because of the issue where in the nested design, taking individual exceptions out of an ExceptionGroup breaks tracebacks.
This also seems like something that isn't hard to work around. The obvious way would involve shared (parts of) tracebacks, but even that isn't required.
multiple-except-executions option should be rejected because users have written code like 'except SomeError: ...' with the expectation that the 'except' clause would run exactly once.
That is indeed a problem, because some error handlers would do bad things if rerun.
The problem is, *all* our options for how 'except' should interact with ExceptionGroups will somehow break previous expectations.
agreed.
Concretely: imagine you have a pre-existing 'except SomeError', and some new code inside the 'try' block raises some number of 'SomeError's wrapped in an ExceptionGroup. There are three options: - Execute the 'except' block multiple times. This breaks the expectation that it should be executed at most once.
Do we know how bad this really is? A second rollback or cancel or endConnection won't do what is expected, but they normally won't do much harm either. A second logError does the right thing.
- Execute the 'except' block exactly once. But if there are multiple SomeError's, this require they be grouped and delivered as a single exception, which breaks typing.
I'm not worried about typing, but I am worried about silently dropping all but the "first" SomeError. On the other hand, in practice were they all (including the "first") dropped before, in favor of some sort of superseding asyncCancelledError?
- Execute the 'except' block zero times. This is what the current PEP chooses, and breaks the expectation that 'except SomeError' should catch 'SomeError'.
To me, this seems the worst of the three options, but ... maybe it is effectively the status quo often enough that it becomes the least bad?
I'm confused about the flattening suggestion - above you talk about "flat EG", but below about tracebacks. It's not clear to me whether you want EG to be flat (ie no nesting of EGs) or just the traceback to be flat (but you can still have a nested EG). Hmm, I was thinking about making both of them flat, so no nested EGs. In all my designs, the only reason I ever had nesting was because I couldn't figure out any other way to make the tracebacks work. Do you have some other motivation for wanting nesting? If so that would be interesting, because it might point to why we're talking past each other and help us understand the problem better...
When I view this as strictly a typing problem, it matters which exceptions got joined at which junction; the shape of the tree is part of the type. When I view this as a production support programmer, I really don't care about that ... I only care what triggered each SomeException so that I can account for all the bad data instead of just one piece. Having to dig through multiple layers of grouping to get to each original SomeException separately is just an annoyance that *will* cause juniors to sometimes miss part of the bad data. ("Open the door and check for problems" is a lot easier to learn and remember than a recursive "Open the door and check for problems or other doors, and then repeat on each door you find.") -jJ