-
Notifications
You must be signed in to change notification settings - Fork 4
Introducing try..except* #4
Comments
Very nice! Only a few nits. Why do we need I think your English description is less precise than my example in #3 (comment). For example you don't specify in which order multiple handlers are run if more than one is run (mine makes it clear that they run from top to bottom) and I also think we should be careful to clarify what happens if you write e.g.
(To get the desired effect, the more specific handler should come first.) Another thing: when an
Shouldn't this use bare Also probably should link to python-trio/trio#611 (Trio MultiError v2). |
Awesome!
This could sound like
It's the other way around (as in your example) - the exception that just happened has context which is the ExceptionGroup that we are now handling.
It might be worth reiterating here what happens to exceptions raised within the except clauses (they are added to a separate list and merged with the unhandled exceptions at the end so they cannot be handled by another except clause of this try.)
We considered (but I don't think we've decided yet) to make it a new type which is a superclass of Still open: How control flow statements (return, continue, break) interact with a try-*except clause. Return should take us to |
Actually yes, I now think so too. Let me shed some light on why I added this syntax.
So yeah, let's remove
Right, I'll add a variant of this example to the proposal.
If there's a
Yes. This is a remnant of my
Good catch! If you see my reply to @gvanrossum you'll find why ;) I'll fix this.
Another great catch, will fix.
Alright, I'll clarify.
Since they
I'll add examples. |
I would prefer not to do that. I will be backwards incompatible, since there is likely tons of code out there that either catches I know @njsmith said he would like User code may have no business interacting with |
I'm also not entirely sure why we'd need that. If we make it completely different from exceptions then And I also don't see a good enough argument to deepen the Python exceptions hierarchy with try:
boostrap_app()
except BaseException as e:
logger.log(e)
raise is better to continue working when exception groups are around. |
How bad would it be if we just included these in the translated code, assuming the translated code is something like
Noting that there actually must be another try statement to deal with exceptions coming out of However, another way to think of these is that they raise a special kind of pseudo-exception that isn't catchable but that, when it is re-raised at the end of the
Which will it do? The first semantics I propose will clearly do |
Does the first option mean that break/continue acts on a loop enclosing this try-except block? So either break or continue means that no further except clauses are executed (but finally of course is)? |
How about we prohibit |
Agreed. I think that for now, |
If we forbid break/continue we should also disallow return, which has the same issues. But while break/continue in except blocks feel pretty academic, return does not. |
This embeds [1] and [2] pretty much verbatim (with 79 characters per line rule applied.) [1] #3 (comment) [2] #4
I decided that it would be much more productive to use GitHub PRs to start tracking changes to the proposal. I've created a document combining my motivation for the |
The PR: #5 |
I'm closing this issue for now. We'll open new ones to discuss specific aspects of the proposal, that now lives at https://github.com/python/exceptiongroups/blob/master/except_star.md. |
The design discussed in this issue has been consolidated in https://github.com/python/exceptiongroups/blob/master/except_star.md.
Disclaimer
ExceptionGroup
name in this issue, even though there are other alternatives, e.g.AggregateException
. Naming of the "exception group" object is outside of the scope of this issue.try..except
construct, shortly called "except*".ValueError
propagating through the stack is "naked".ExceptionGroup
would be an iterable object. E.g.list(ExceptionGroup(ValueError('a'), TypeError('b')))
would be equal to[ValueError('a'), TypeError('b')]
ExceptionGroup
won't be an indexable object; essentially it's similar to Pythonset
. The motivation for this is that exceptions can occur in random order, and letting users writegroup[0]
to access the "first" error is error prone. The actual implementation ofExceptionGroup
will likely use an ordered list of errors though.ExceptionGroup
will be a subclass ofBaseException
, which means it's assignable toException.__context__
and can be directly handled withexcept ExceptionGroup
.try..except
will not be modified.Syntax
We're considering to introduce a new variant of the
try..except
syntax to simplify working with exception groups:The new syntax can be viewed as a variant of the tuple unpacking syntax. The
*
symbol indicates that zero or more exceptions can be "caught" and processed by oneexcept *
clause.We also propose to enable "unpacking" in the
raise
statement:Semantics
Overview
The
except *SpamError
block will be run if thetry
code raised anExceptionGroup
with one or more instances ofSpamError
. It would also be triggered if a naked instance ofSpamError
was raised.The
except *BazError as e
block would aggregate all instances ofBazError
into a list, wrap that list into anExceptionGroup
instance, and assign the resultant object toe
. The type ofe
would beExceptionGroup[BazError]
. If there was just one naked instance ofBazError
, it would be wrapped into a list and assigned toe
.The
except *(BarError, FooError) as e
would aggregate all instances ofBarError
orFooError
into a list and assign that wrapped list toe
. The type ofe
would beExceptionGroup[Union[BarError, FooError]]
.Even though every
except*
star can be called only once, any number of them can be run during handling of anExceptionGroup
. E.g. in the above example, bothexcept *SpamError:
andexcept *(BarError, FooError) as e:
could get executed during handling of oneExceptionGroup
object, or all of theexcept*
clauses, or just one of them.It is not allowed to use both regular
except
clauses and the newexcept*
clauses in the sametry
block. E.g. the following example would raise aSyntaxErorr
:Exceptions are mached using a subclass check. For example:
could output:
New raise* Syntax
The new
raise *
syntax allows to users to only process some exceptions out of the matched set, e.g.:The above code ignores all
EPIPE
OS errors, while letting all others propagate.raise *
syntax is special: it effectively extends the exception group with a list of errors without creating a newExceptionGroup
instance:A regular raise would behave similarly:
raise *
accepts arguments of typeIterable[BaseException]
.Unmatched Exceptions
Example:
The above code would print:
And then crash with an unhandled
KeyError('e')
error.Basically, before interpreting
except *
clauses, the interpreter will have an exception group object with a list of exceptions in it. Everyexcept *
clause, evaluated from top to bottom, can filter some of the exceptions out of the group and process them. In the end, if the exception group has no exceptions left in it, it wold mean that all exceptions were processed. If the exception group has some unprocessed exceptions, the current frame will be "pushed" to the group's traceback and the group would be propagated up the stack.Exception Chaining
If an error occur during processing a set of exceptions in a
except *
block, all matched errors would be put in a newExceptionGroup
which would have its__context__
attribute set to the just occurred exception:It's also possible to explicitly chain exceptions:
See Also
ExceptionGroup
type by @iritkatriel tracked here: GitHub - iritkatriel/cpython at exceptionGroupThe text was updated successfully, but these errors were encountered: