FTR: nobody on this long thread so far has suggested that there are no valid use cases for `except Exception`.
Thank you for turning to what happens with 'except ValueError' when an ExceptionGroup[ValueError] is raised, this is important. 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") On Fri, Feb 26, 2021 at 5:40 AM Nathaniel Smith <n...@pobox.com> wrote: > On Thu, Feb 25, 2021 at 2:13 PM Guido van Rossum <gu...@python.org> wrote: > > > > So is "fail-fast if you forget to handle an ExceptionGroup" really a > feature? (Do we call this out in the PEP?) > > > > We may believe that "except Exception" is an abuse, but it is too common > to dismiss out of hand. I think if some app has e.g. a main loop where they > repeatedly do something that may fail in many ways (e.g. handle a web > request), catch all errors and then just log the error and continue from > the top, it's a better experience if it logs "ExceptionGroup: <message> > [<list of subexceptions>]" than if it crashes. > > Yeah, 'except Exception' happens a lot in the wild, and what to do > about that has been a major sticking point in the ExceptionGroup > debates all along. I wouldn't say that 'except Exception' is an abuse > even -- what do you want gunicorn to do if your buggy flask app raises > some random exception? Crash your entire web server, or log it and > attempt to keep going? (This is almost your example, but adding in the > part where gunicorn is reliable and well-respected, and that its whole > job is to invoke arbitrarily flaky code written by random users.) > 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). > > == NEW IDEA THAT MAYBE SOLVES BOTH PROBLEMS == > > Proposal: > > - any time an unwinding ExceptionGroup encounters a traditional > try/except, then it gets replaced with a RuntimeError whose __cause__ > is set to the original ExceptionGroup and whose first traceback entry > points to the offending try/except block > > - CUTE BIT I ONLY JUST THOUGHT OF: this substitution happens right > *before* we start evaluating 'except' clauses for this try/except > > So for example: > > If an ExceptionGroup hits an 'except Exception': The ExceptionGroup is > replaced by a RuntimeError. RuntimeError is an Exception, so the > 'except Exception' clause catches it. And presumably logs it or > something. This way your log contains both a notification that you > might want to switch to except* (from the RuntimeError), *along with* > the full original exception details (from the __cause__ attribute). If > it was an ExceptionGroup(KeyboardInterrupt), then it still gets caught > and that's not so great, but at least you get the RuntimeError to > point out that something has gone wrong and tell you where? > > If an ExceptionGroup(ValueError) hits an 'except ValueError': it > doesn't get caught, *but* a RuntimeError keeps propagating out to tell > you you have a problem. And when that RuntimeError eventually hits the > top of your program or ends up in your webserver logs or whatever, > then the RuntimeError's traceback will point you to the 'except > ValueError' that needs to be fixed. > > If you write 'except ExceptionGroup': this clause is a no-op that will > never execute, because it's impossible to still have an ExceptionGroup > when we start matching 'except' clauses. (We could additionally emit a > diagnostic if we want.) > > If you write bare 'except:', or 'except BaseException': the clause > always executes (as before), but they get the RuntimeError instead of > the ExceptionGroup. If you really *wanted* the ExceptionGroup, you can > retrieve it from the __cause__ attribute. (The only case I can think > of where this would be useful is if you're writing code that has to > straddle both old and new Python versions *and* wants to do something > clever with ExceptionGroups. I think this would happen if you're > implementing Trio, or implementing a higher-level backport library for > catching ExceptionGroups, something like that. So this only applies to > like half a dozen users total, but they are important users :-).) > > -n > > -- > Nathaniel J. Smith -- https://vorpus.org >
_______________________________________________ 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/VKLL7CIWBVIT26TAX5DVXRIWZF4AJJTJ/ Code of Conduct: http://python.org/psf/codeofconduct/