_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/L5Q27DVKOKZCDNCAWRIQVOZ5DZCZHLRM/
Code of Conduct: http://python.org/psf/codeofconduct/
So a statement that the current handling is "cumbersome" and "unintuitive" is unconvincing.
If this results in lots of boilerplate code with isinstance(),
filter() can be changed to e.g. accept a dict of exception types
and handlers. Actually, I wonder why you didn't do that already if
handling specific exception types and reraising the rest is the
standard procedure!
Then the code would be reduced to:
try:
<smth>
except (MultiError, ValueError) as e:
MultiError.filter({ValueError: lambda
_: None},
e)
If some code doesn't know about MultiError's, it should handle
one like any other unknown exception that it cannot do anything
intelligent about.
If it wishes to handle them, you need to split MultiError into a
separate library that anyone could use without having to pull the
entire `trio`.
_______________________________________________ Python-Dev mailing list -- pytho...@python.org To unsubscribe send an email to python-d...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/L5Q27DVKOKZCDNCAWRIQVOZ5DZCZHLRM/ Code of Conduct: http://python.org/psf/codeofconduct/
-- Regards, Ivan
On Tue, Feb 23, 2021 at 3:49 PM Damian Shaw <damian.p...@gmail.com> wrote:
Firstly, if I have a library which supports multiple versions of Python and I need to catch all standard exceptions, what is considered the best practise after this PEP is introduced?Currently I might have code like this right now:try:... # Codeexcept Exception as e:... # Logic to handle exception
Hi Damian,Catching all exceptions in this way is not a common pattern. Typically you have an idea what kind of exceptions you expect to get from an operation, and which of those exceptions you are interested in handling. Most of your try/except blocks will be targeted, like handling KeyError from d[k], an operation that will never raise an ExceptionGroup.ExceptionGroups will only be raised by specific APIs which will advertise themselves as such. We don't expect that there will be a need for blanket migrations of "except T" to "except (T, ExceptionGroup)" to "except *T".Irit
Firstly, if I have a library which supports multiple versions of Python and I need to catch all standard exceptions, what is considered the best practise after this PEP is introduced?Currently I might have code like this right now:try:... # Codeexcept Exception as e:... # Logic to handle exception
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/XAPO475TVFICD77M3VRLC7OKA5O7WTOT/
--- a/Lib/tempfile.py+++ b/Lib/tempfile.py
@@ -819,8 +819,14 @@ def __repr__(self):
def __enter__(self):
return self.name
- def __exit__(self, exc, value, tb):
- self.cleanup()
+ def __exit__(self, exc_cls, exc_value, tb):
+ try:
+ self.cleanup()
+ except Exception as clean_exc:
+ if exc_value is not None:
+ raise ExceptionGroup('Exception occurred during cleanup', [exc_value, clean_exc])
+ else:
+ raise
def cleanup(self):
if self._finalizer.detach():
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/L5Q27DVKOKZCDNCAWRIQVOZ5DZCZHLRM/
What is the motivation for returning `None` on empty splits? I feel like this creates an unnecessary asymmetry. I don't personally have a use case for the this feature so I may be missing something but it seems like it would force an annoying pattern:
```try:
foo()
except ExceptionGroup as eg:
g1, g2 = eg.split(predicate)
for e in g1:
handle_true(e)
for e in g2:
handle_false(e)
```
Also this creates an subtle difference with subgroup:
```
g1, g2 = eg.split(predicate)
h1, h2 = eg.subgroup(predicate), eg.subgroup(lambda e: not predicate(e))
assert g1 == h1 and g2 == h2 # only true if `None not in {g1, g2}`
```
> The `ExceptionGroup` class is final, i.e., it cannot be subclassed.
What's the rationale for this?
> It is possible to catch the ExceptionGroup type with except, but not
with except* because the latter is ambiguous
What about `except *(TypeError, ExceptionGroup):`?
> Motivation: Errors in wrapper code
This use case sticks out a bit: it's the only one where ExceptionGroup
doesn't represent joining equivalent tasks.
Consider code similar to bpo-40857:
try:
with TemporaryDirectory() as tempdir:
os.rmdir(tempdir)
n = 1 / 0
except ArithmeticError:
# that error can be safely ignored!
pass
Instead of a FileNotFoundError with ArithmeticError for context you'd
now get an ExceptionGroup. Neither is handled by `except
ArithmeticError`. Where is the win?
> Motivation: Multiple failures when retrying an operation
This is somewhat similar to the TemporaryDirectory, except there's no
`with` block that feels like it should be "transparent" w.r.t. user errors.
If I currently have:
try:
create_connection(*addresses)
except (Timeout, NetworkNotConnected):
# that's OK, let's try later
pass
what should happen after Python 3.10? Apart from adding a new function,
I can see two possible changes:
- create_connection() starts always raising ExceptionGroup on error,
breaking backwards compatibility in the error case
- create_connection() starts only raising ExceptionGroup only for 2+
errors, breaking backwards compatibility in the 2+ errors case
Both look like heisenbug magnets. IMO, the second one is worse; "code
which is now *potentially* raising ExceptionGroup" (as mentioned in the
Backwards Compatibility section; emphasis mine) should be discouraged.
Arguably, this here is a problem with the create_connection function:
the PEP adds a better way how it could have been designed, and that is
virtuous. Still, having it in Motivation might be misleading.
> long term plan to replace `except` by `catch`
Whoa! Is that a real plan?
On 2/22/21 4:24 PM, Irit Katriel via Python-Dev wrote:
> We would like to request feedback on PEP 654 -- Exception Groups and
> except*.
>
> https://www.python.org/dev/peps/pep-0654/
>
> It proposes language extensions that allow programs to raise and
> handle multiple unrelated exceptions simultaneously, motivated by the
> needs of asyncio and other concurrency libraries, but with other use
> cases as well.
It sounds like the long-term goal is to move away from `except` and
replace it with `except *` -- is that correct?
A common example I see from the GitHub search is a catch all exception on some third party API. If the user wants to catch all exceptions from calling a third party API why would they want to let an ExceptionGroup go unhandled in the future? Once ExceptionGroups are introduced then libraries will surely implement them where appropriate and users would want to continue having their board exception handling working, would they not?
MultiError
is an Exception
, then it can only contain Exception
subclasses, because otherwise a MultiError(KeyboardInterrupt)
could get caught by an except Exception
handler, which would be bad." But those notes are from back in 2018, not from the 2020 core dev sprint when we got much closer to the current design. So I'm not sure the reasoning still applies. (Yury? Can you help us out here?)I don't actually know if the code I wrote in the first email is correct though, would it catch all standard Exception / ExceptionGroups across Python 3.x if this PEP was implemented in the future? And would it be best practice for this pattern? If so it seems it would require many users to rewrite their code as it adds boilerplate and nuance for a pattern that is a relatively simple and very commonly used right now.I guess summarized my commentary is please don't dismiss this pattern out of hand, which this PEP currently disrupts, it is used widely in Python right now and has many valid use cases. I don't have anything further to add so I won't continue this discussion and I assume anything further you have to add to be considered and more informed than my own opinion.
Thanks,Damian
try:
- In https://www.python.org/dev/peps/pep-0654/#programming-without-except, the natural way isn't shown:
<smth>
except (MultiError, ValueError) as e:
def _handle(e):
if isinstance(e, ValueError):
return None
else:
return exc
MultiError.filter(_handle,e)
So a statement that the current handling is "cumbersome" and "unintuitive" is unconvincing.
If this results in lots of boilerplate code with isinstance(), filter() can be changed to e.g. accept a dict of exception types and handlers. Actually, I wonder why you didn't do that already if handling specific exception types and reraising the rest is the standard procedure!
Then the code would be reduced to:try:
<smth>
except (MultiError, ValueError) as e:
MultiError.filter({ValueError: lambda _: None},
e)
- https://github.com/python-trio/trio/issues/611 says that it somehow causes problems with 3rd-party libraries -- but there's no explanation how -- either there or in the PEP.
If some code doesn't know about MultiError's, it should handle one like any other unknown exception that it cannot do anything intelligent about.
If it wishes to handle them, you need to split MultiError into a separate library that anyone could use without having to pull the entire `trio`.
Hi Steve,Thank you for trying out the implementation. Please do let me know about bugs you find.
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.)
try: low_level_os_operation() except *OSerror as errors: raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None
OTOH we might reconsider deriving ExceptionGroup from BaseException -- maybe it's better to inherit from Exception? I don't think the PEP currently has a clear reason why it must derive from BaseException. I believe it has to do with the possibility that ExceptionGroup might wrap a BaseException instance (e.g. KeyboardInterrupt or SystemExit).
Petr: What about except *(TypeError, ExceptionGroup):?
Irit: Good question. We should block that as well.
But https://www.python.org/dev/peps/pep-0654/#backwards-compatibility
seems to explicitly recommend the very similar:
except (Exception, ExceptionGroup):
Please include an example for:
except *ExceptionGroup: pass
The "no subclasses" seems pretty severe, particularly if you can't even use marker attributes because it isn't clear when a different instance of container ExceptionGroup will be substituted.
The justification for this restriction was that .split() had to be be able to instantiate ... wouldn't it be enough to just say that subclasses need a compatible __init__ and __new__ for that reason, and then leave it to consenting adults?
try: async with trio.open_nursery() as nursery: # Make two concurrent calls to child() nursery.start_soon(child) nursery.start_soon(child) except ValueError: pass
We would like to request feedback on PEP 654 -- Exception Groups and except*.It proposes language extensions that allow programs to raise and handle multiple unrelatedexceptions simultaneously, motivated by the needs of asyncio and other concurrency libraries,but with other use cases as well.
* A new standard exception type, ExceptionGroup, to represent multiple exceptions withshared traceback.* Updates to the traceback printing code to display (possibly nested) ExceptionGroups.
* A new syntax except* for handling ExceptionGroups.A reference implementation (unreviewed) can be found at:
Thank you for your help
Are you saying that
except *ValueError as e:
will catch
ValueError
and
ExceptionGroup(ValueError)
but miss
ExceptionGroup(ExceptionGroup(ValueError))
?
If it (compatible __new__ and __init__) needs to be checked at definition time, just try create an instance passing the same arguments you would pass to the base class. If the creation it doesn't raise an exception, that is good enough.
This isn't about theoretical type safety against malice; it is about defining the minimal protocol for an ExceptionGrouping that has to be supported by someone who wants something other than the default flavor.
After reading through the PEP and skimming the code (but I didn't build it), something I didn't see: What happens to a currently conforming except check?try: async with trio.open_nursery() as nursery: # Make two concurrent calls to child() nursery.start_soon(child) nursery.start_soon(child) except ValueError: passI've removed the * from the example: Say the interface was built for 3.7, but the "trio" module has been upgraded to use ExceptionGroups which can't fall back to a standalone exception.Silently hand back the first exception, or the first matching exception? Deprecation warning? Silently skip? Context-specific error? Run the default error handler?I think that deserves a statement in the PEP.
Here's a potentially alternative plan, which is also complex, but doesn't require asyncio or other use cases to define special classes. Let's define two exceptions, BaseExceptionGroup which wraps BaseException instances, and ExceptionGroup which only wraps Exception instances. (Names to be bikeshedded.) They could share a constructor (always invoked via BaseExceptionGroup) which chooses the right class depending on whether there are any non-Exception instances being wrapped -- this would do the right thing for split() and subgroup() and re-raising unhandled exceptions.Then 'except Exception:' would catch ExceptionGroup but not BaseExceptionGroup, so if a group wraps e.g. KeyboardError it wouldn't be caught (even if there's also e.g. a ValueError among the wrapped errors).Pseudo-code:class BaseExceptionGroup(BaseException):def __new__(cls, msg, errors):if cls is BaseExceptionGroup and all(isinstance(e, Exception) for e in errors):cls = ExceptionGroupreturn BaseException.__new__(cls, msg, errors)class ExceptionGroup(Exception, BaseExceptionGroup):pass
[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? :-)
Hm, a different idea: maybe it's simple enough that we can just add an example showing how to do this? Then people can tailor that e.g. to use various traversal orders. (We could just link to the code in traceback.py, but it probably is full of distractions.)
On Fri, Feb 26, 2021 at 2:00 AM Guido van Rossum <gu...@python.org> wrote:Then we should get some more opinions on this. I think it's the best idea so far for kindness towards code using `except Exception:`.
I agree that it's the best idea so far.If someone says 'except Exception' they better mean it because we'll believe them, and if someone forgets to handle an ExceptionGroup then they have a bug and that's how it is.
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.
Which brings me to the question, do you have a branch that matches the PEP yet?
The implementation matches the PEP less and less every day because the PEP is developing faster than the implementation.But these aren't differences that are more than a technicality to fix (rename things, move an error from runtime to the parser, things like that).The except* implementation is probably pretty close to the PEP because it's the most recent bit.
Then we should get some more opinions on this. I think it's the best idea so far for kindness towards code using `except Exception:`.
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.
Which brings me to the question, do you have a branch that matches the PEP yet?
On Fri, Feb 26, 2021 at 5:05 AM Irit Katriel <iritk...@googlemail.com> wrote:
> I'm not sure it's safe to assume that it is necessarily a programming error, and that the interpreter can essentially break the program in this case.
> Is this not allowed?
>
> try:
> try:
> obj.func() # function that raises ExceptionGroups
> except AttributeError:
> logger.info("obj doesn't have a func")
> except *(AttributeError, SyntaxError):
> logger.info("func had some problems")
I'd be fine with disallowing that. The intuition is that things will
be simplest if ExceptionGroup is kept as transparent and meaningless
as possible, i.e. ExceptionGroup(ValueError) and ValueError mean
exactly the same thing -- "some code inside this block raised
ValueError" -- and ideally should be processed in exactly the same
way.
Yury/I/others did discuss the idea of a
BaseExceptionGroup/ExceptionGroup split a lot, and I think the general
feeling is that it could potentially work, but feels like a
complicated and awkward hack, so no-one was super excited about it.
For a while we also had a compromise design where only
BaseExceptionGroup was built-in, but we left it non-final specifically
so asyncio could define an ExceptionsOnlyExceptionGroup.
Another somewhat-related awkward part of the API is how ExceptionGroup
and plain-old 'except' should interact *in general*. The intuition is
that if you have 'except ValueError' and you get an
'ExceptionGroup(ValueError)', then the user's code has some kind of
problem and we should probably do.... something? to let them know? One
idea I had was that we should raise a RuntimeError if this happens,
sort of similar to PEP 479. But I could never quite figure out how
this would help (gunicorn crashing with a RuntimeError isn't obviously
better than gunicorn crashing with an ExceptionGroup).
Excuse me if I post here. Maybe is a stupid question: why, instead of
introducing except*, Python can't extend the functionality of except,
so it can do what except* would do?
> Is this not allowed?
>try:
> try:
> obj.func() # function that raises ExceptionGroups
> except AttributeError:
> logger.info("obj doesn't have a func")
>except *(AttributeError, SyntaxError):
> logger.info("func had some problems")
Allowed, but probably in error ... no AttributeError will get through to the except * unless it happened inside the except AttributeError handler. Did you mean:
On Fri, Feb 26, 2021 at 3:18 PM Marco Sulla <Marco.Sul...@gmail.com> wrote:Excuse me if I post here. Maybe is a stupid question: why, instead of
introducing except*, Python can't extend the functionality of except,
so it can do what except* would do?Good question. Here's an example:```try:. . .except OSError as err:if err.errno != ENOENT:raise. . .```If this would catch ExceptionGroup(OSError), the `err` variable would be an ExceptionGroup instance, which does not have an `errno` attribute.(Irit: Does the PEP answer this question? I couldn't quickly find it in the rejected ideas. I think it's a reasonable question and we should answer it, either in the Rationale or in Rejected Ideas.)
While I don't particularly mind if we get ExceptionGroup, giving
it special magical semantics, and especially new syntax, seems like
bringing a massively overpowered weapon to bear on something that
will only be used rarely.
Handling multiple exceptions from an ExceptionGroup could be done
using a loop with pattern matching, so is except* really justified?
On Fri, 26 Feb 2021 at 23:36, Jim J. Jewett <jimjj...@gmail.com> wrote:
>
> Whenever I've used except Exception or stronger, it was a sanitary barrier around code that might well do unpredictable or even stupid things. Adding a new kind of exception that I hadn't predicted -- including ExceptionGroup -- would certainly fit this description, and I want my driver loop to do what I told it. (Probably log an unexpected exception, and continue with the next record. I honestly don't even want a page, let alone a crash, because data from outside that barrier ... is often bad, and almost never in ways the on-call person can safely fix. And if they don't have time to find it in the logs, then it isn't a priority that week.)
This is my biggest concern. Disclaimer: I've yet to read the PEP,
because async makes my head hurt, but I am aware of some of the
background with Trio. Please take this as the perspective of someone
thinking "I don't use async/await in my code, can I assume this
doesn't affect me?"
For me, the biggest requirement I would have is that if I have code like:
def safe_fn():
try:
do_something_funky()
return True
except Exception:
print("Well, that was a shame...")
return False
then I am intending to guarantee that calling safe_fn() will never
raise an exception. Obviously, the example is a toy (in reality, I'd
log the error, skip a record, whatever) but that's not the point here
- the point is that I consider it reasonable to expect `except
Exception` to be a hard barrier that allows me to be sure I've covered
everything.
My worry is that if ExceptionGroup exceptions are *not* trapped by
`except Exception`, then code like this is no longer safe. And by
making ExceptionGroup a BaseException subclass, that's what will
happen.
Ultimately, there's a genuine use case for code that says "whatever
happens in the code I call, this is my last line of defense and I want
to be as near to 100% sure as I can be that I regain control at this
point". At the moment, that is spelled `except Exception`. If you're
proposing to change that, even if it's just to require that it be
spelled differently, you're going to break quite a lot of code - and
worse, the code you're breaking will be code that has taken care to
ensure it's written safely, so you're penalising people who *tried to
get stuff right*, which IMO is the worst type of breakage.
I will at some point read the PEP fully, to better understand the
async side of the story, but for now please consider this as the
perspective of someone who doesn't expect to care about async, and
therefore wants to feel safe in assuming this PEP won't affect them.
Paul
PS If you're thinking "but if you were using async, you'd know about
it", consider that I could be using a PyPI library that, for example,
returned stock values. That library switches to using async, and has a
bug that lets an exception group escape. My `except Exception` code is
designed *precisely* to protect me against such bugs, without me
needing to know anything about how the library works...
On Fri, 26 Feb 2021 at 23:36, Jim J. Jewett <jimjj...@gmail.com> wrote:Whenever I've used except Exception or stronger, it was a sanitary barrier around code that might well do unpredictable or even stupid things. Adding a new kind of exception that I hadn't predicted -- including ExceptionGroup -- would certainly fit this description, and I want my driver loop to do what I told it. (Probably log an unexpected exception, and continue with the next record. I honestly don't even want a page, let alone a crash, because data from outside that barrier ... is often bad, and almost never in ways the on-call person can safely fix. And if they don't have time to find it in the logs, then it isn't a priority that week.)This is my biggest concern. Disclaimer: I've yet to read the PEP, because async makes my head hurt, but I am aware of some of the background with Trio. Please take this as the perspective of someone thinking "I don't use async/await in my code, can I assume this doesn't affect me?"
I haven't read the PEP either. But I assume it could (should?) affect anyone managing multiple simultaneous things in Python:
It seems to me that any of those could raise multiple heterogeneous exceptions, and Python doesn't currently provide a mechanism to manage this situation. My dim understanding is that ExceptionGroup proposes a mechanism to handle exactly this thing.
Cheers,
/arry
try: low_level_os_operation() except *OSerror as errors: raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None
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 2:40 AM Irit Katriel <iritk...@googlemail.com> wrote: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).Is this a typo? Did you mean "If you flatten [it] and create a new list... you *do* lose metadata (unless ... depth 1)"?
On 28Feb2021 10:40, Irit Katriel <iritk...@googlemail.com> wrote:
>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.
Wouldn't it be safe if the ExceptionGroup were immutable, as you plan?
Or have you another hazard in mind?
But all that said, being able to iterable the subexceptions seems a
natural way to manage that:
unhandled = []
try:
.........
except *OSError as eg:
for e in eg:
if an ok exception:
handle it
else:
unhandled.append(e)
if unhandled:
raise ExceptionGroup("unhandled", unhandled)
I have the following concerns with the pattern above:
There's no way to make a _new_ ExceptionGroup with the same __cause__
and __context__ and message as the original group: not that I can't
assign to these, but I'd need to enuerate them; what if the exceptions
grew new attributes I wanted to replicate?
This cries out for another factory method like .subgroup but which makes
a new ExceptionGroup from an original group, containing a new sequence
of exceptions but the same message and coontext. Example:
unhandled_eg = eg0.with_exceptions(unhandled)
I don't see a documented way to access the group's message.
Cheers,
Cameron Simpson <c...@cskk.id.au>
Let's turn this on its head:
- what specific harm comes from giving EGs container truthiness for size
testing?
- what specific harm comes from returning an empty EG from split on no
match instead of None?
- what specific harm comes from supporting iteration with a caveat about
metadata in the docstring, and maybe a recommendation of subgroup?
- if I wanted to override subgroup and split to not return None, is that
even possible with the current ? i.e. can I make a clean metadata
preserved EG from an empty list? For example:
eg2 = eg.subgroup(lambda e: False)
Does that get me None, or an empty group? If the latter, I can roll my
own subclass with my desired features. If not, I can't AFAICT.
EGs _have_ a .errors attribute which has all these aspects, why not
expand it to the class as a whole?
You seem very happen to implement 80% of what I want using callbacks
(lambda e: ...), but I find explicit iteration much easier to read. I
rarely use filter() for example, and often prefer a generator expression
of list comprehension.
Just to be on the safe side here, I would want to asked a little
bit further.
1) What is the right "except pattern" when ExceptionGroup is introduced for the use cases described above (remote or concurrent python processes)?
If you want to catch the whole ExceptionGroup and format its traceback, then you can just do "except ExceptionGroup as eg" and then traceback.print_exception(eg).
The except* syntax is for when you want to handle only certain types of exceptions from the group, selectively.
Just to make sure, I understand this completely.
In order to make it more tangible here is an example from the stdlib:
As you can see, BaseException is caught multiple times as the only except-clause. _sendback_result(...) then used to transfer the return_val/exception back to parent process.
How is the supposed way of handling a raised ExceptionGroup?
a) will the existing code simply work as it?
b) if not what are the changes to this lib specifically as a
blueprint example for others
Reading from the other subthread for this PEP, I am not 100% sure now that "except BaseException" will suffice if ExceptionGroup inherits from Exception instead of BaseException because it seems that ExceptionGroup somehow is handled specially. So, why I try to approach this very issue with existing code. Once that is clear, it should be easy for everybody to apply the same pattern to their code. Here is the copy from github:
try:
r = call_item.fn(*call_item.args, **call_item.kwargs)
except BaseException as e:
exc = _ExceptionWithTraceback(e, e.__traceback__)
_sendback_result(result_queue, call_item.work_id,
exception=exc)
else:
_sendback_result(result_queue, call_item.work_id, result=r)
del r
Maybe even _sendback_result could benefit from using
ExceptionGroup itself. You can see there another "except
BaseException" in case of an error. But that's maybe a different
topic.
2) What is the recommended approach of printing the traceback potentially incorporating multiple tracebacks - I couldn't find it in the PEP and tracebacks are a really neat tool for error hunting.
Part of the proposal of the PEP is that we teach the builtin traceback formatting code to display ExceptionGroups with all that information.
As long as there's the possibility to receive the usual
(two-line) entry-based list, I guess that is enough for every one
to work with it (e.g. hiding unnecessary tb entries).
The reference implementation has this, and the examples in the PEP were produced with it. Some of the examples (the early ones) show exceptions that were never raised so there is no traceback. But if you scroll down to the later examples, you see the output for exceptions with tracebacks, cause, context etc.
Ah I see them now. Thank you. :-)
We didn't put too much effort into making the traceback output pretty, so at the moment it's a draft. If there is an ascii artist out there who has suggestions on improving this, that would be great.
Well, yes. It's not really pretty. I would recommend a tree-based solution such as the following:
I used a similar pattern for the remote-execution lib and
received good user feedback (even requesting color encoding for
each layer of the tree (not the text), so people know what belongs
together). Besides colors, I used
https://en.wikipedia.org/wiki/Box-drawing_character but I guess
pipes, dashes and pluses could suffice for CPython.
One other remark from my side here: in the example of the PEP
there seem to be missing a space before 'File "<stdin>"'.
Comparing outer tbs with inner tbs, you can see that the "F" of
"File" is not underneath the a of "Traceback" but underneath the
"r" of it.
Regards,
Sven
Hey Irit,
cool proposal.
I just have two questions regarding "except Exception" or "except BaseException" as it is used e.g. by concurrent.futures.process (def _process_worker) from the stdlib.
Almost similarly, I maintain a library using this pattern to wrap/unwrap exceptions from remote Python processes to create nicely formatted tracebacks (also recursively of course if needed) at the calling python process.
Usually these exceptions are wrapped, transferred to the parent process, there is the current call stack added, then reraised as a different exception, and so on and so forth if a chain of parents exist. The outermost parent process takes care of printing the tb.
My two questions regarding PEP 654:
1) What is the right "except pattern" when ExceptionGroup is introduced for the use cases described above (remote or concurrent python processes)?
2) What is the recommended approach of printing the traceback potentially incorporating multiple tracebacks - I couldn't find it in the PEP and tracebacks are a really neat tool for error hunting.
On 23 Feb 2021, at 00:24, Irit Katriel via Python-Dev <pytho...@python.org> wrote:Hi all,We would like to request feedback on PEP 654 -- Exception Groups and except*.It proposes language extensions that allow programs to raise and handle multiple unrelatedexceptions simultaneously, motivated by the needs of asyncio and other concurrency libraries,but with other use cases as well.* A new standard exception type, ExceptionGroup, to represent multiple exceptions withshared traceback.* Updates to the traceback printing code to display (possibly nested) ExceptionGroups.
* A new syntax except* for handling ExceptionGroups.A reference implementation (unreviewed) can be found at:
Thank you for your helpKind regardsIrit, Yury & Guido
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Hey Irit,
cool proposal.
I just have two questions regarding "except Exception" or "except BaseException" as it is used e.g. by concurrent.futures.process (def _process_worker) from the stdlib.
Almost similarly, I maintain a library using this pattern to
wrap/unwrap exceptions from remote Python processes to create
nicely formatted tracebacks (also recursively of course if needed)
at the calling python process.
Usually these exceptions are wrapped, transferred to the parent process, there is the current call stack added, then reraised as a different exception, and so on and so forth if a chain of parents exist. The outermost parent process takes care of printing the tb.
My two questions regarding PEP 654:
1) What is the right "except pattern" when ExceptionGroup is
introduced for the use cases described above (remote or concurrent
python processes)?
2) What is the recommended approach of printing the traceback
potentially incorporating multiple tracebacks - I couldn't find it
in the PEP and tracebacks are a really neat tool for error
hunting.
Best Regards,
Sven
Hi all,
We would like to request feedback on PEP 654 -- Exception Groups and except*.
It proposes language extensions that allow programs to raise and handle multiple unrelatedexceptions simultaneously, motivated by the needs of asyncio and other concurrency libraries,but with other use cases as well.
* A new standard exception type, ExceptionGroup, to represent multiple exceptions withshared traceback.* Updates to the traceback printing code to display (possibly nested) ExceptionGroups.
* A new syntax except* for handling ExceptionGroups.
A reference implementation (unreviewed) can be found at:
Thank you for your help
Kind regardsIrit, Yury & Guido
Have you considered alternative syntax to the "except*"?I feel that expect* is too easy to confuse with plain except.
As others have said needing to change all except Exception:handlers would be a breaking change for code that I maintain.
I think its accepted that that idiom should continue to be asreliable as it currently is. Can you confirm that I have understoodthe conversation on this point?
I have been thinking about why I would be getting an ExceptionGroupwith, for example, a number of OSError for a handler to deal with.Would it not be a difficult piece of code to write without the context ofeach OSError?What I'm think is that the lower level pieces would be catching the OSErrorcloser to the source of the problem and raising a App/Lib specific exceptionthat provides the context. For example AppFailedToSaveConfigError andAppFailedToSaveDataError as oppose to a two permission OSError's.
With context I can imagine that handling the ExceptionGroup would bea lot easier for the App designer.If that the pattern that emerges then is the complexity of nestedExceptionGroups going to only rarely used?
[...]
I can't imagine people building deep trees of exceptions in practice (at least not on purpose). But some nesting may show up naturally, and we need to support it because otherwise it can get awkward if, for example, asyncio.gather() needs to wrap an exception group that came from a TemporaryDirectory.__exit__().
1. Having now read the PEP, I don't actually see a use case for
grouping BaseExceptions. Why would anyone catch KeyboardInterrupt or
SystemExit and wrap them in a BaseExceptionGroup anyway? It seems to
me that the right thing to do, even in async or parallel code, is to
just propogate the KeyboardInterrupt/SystemExit up to the main
program. Losing a ValueError that happened at the exact same time as
the user pressed Ctrl-C seems like it's not going to be a problem in
practice...
2. Given the above, why even have a means of grouping BaseExceptions
at all? Why not just have ExceptionGroup that can only catch instances
of Exception?