On Wed, 3 Mar 2021 at 12:37, Irit Katriel <iritkatr...@googlemail.com> wrote:
> This is covered the PEP, but TL;DR:
> If we could make "except KeyboardInterrupt" catch 
> BaseExceptionGroup([KeyboardInterrupt]) in a reasonably backwards compatible 
> way then we wouldn't need except*.
[...]
> As you noted, no library is under any obligation to wrap KeyboardInterrupts 
> into the exception groups it raises. You may decide it's a bad idea and not 
> do it.  What we are discussing here is what the language should make 
> possible. We agree that wrapping a BaseException by an Exception is something 
> we should definitely block. When it's wrapping a BaseException by another, 
> new BaseException type, in my view that's ok. You may have a bug where you 
> don't catch an exception you want to catch, because you are using a new API 
> incorrectly. But you won't have bugs where you swallow an exception that you 
> didn't swallow before.

Thanks for the explanation. I understand your point here, and I see
what you're saying. But I have a couple of questions still:

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?

If there really *was* a low-level case where some code absolutely had
to (temporarily) group KeyboardInterrupt, for example, it could be
temporarily wrapped:

class IGotInterrupted(Exception):
    def __init__(self, exc):
        self.exc = exc

def might_be_interrupted():
    try:
        critical_stuff()
    except KeyboardInterrupt as exc:
        raise IGotInterrupted(exc)

def funky_parallel_stuff():
    try:
        do_in_parallel(might_be_interrupted)
    except *IGotInterrupted as e:
        # Please excuse the fact that I don't know the
        # correct way to re-raise here. Might need raise from.
        raise e.exc
    # Do we also need to consider an *ungrouped* IGotInterrupted?
    # That's for the library to decide, and is why not letting it
escape is a good thing...

That would be appropriate for really low-level code, and it would be a
fairly obvious anti-pattern for an exception class like
IGotInterrupted to "escape" so that user code would ever see it.

I guess the argument for grouping BaseExceptions is "why make it
necessary to do complicated wrapping like this?" To which my answer is
"because it should almost never be something you do, and so why add
support for it when it can be done *without* needing explicit support
in the PEP?" And by not having stdlib support for wrapping
BaseExceptions, we're signalling that we don't think people should be
doing it casually...

Paul
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/5LVHBCIWOLYVUYAMFYIZAPEDV7JUVBYS/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to