Bad code exceptions (was PEP-3151 pattern-matching)

On Fri, Apr 8, 2011 at 1:11 PM, Guido van Rossum <guido@python.org> wrote:
The main usage I see for this is "ignore all exceptions except bad programming exceptions". People try to do this now with a bare except clause. 97% of the time, doing this is very ill-advised. My gut reaction is that providing such a thing will encourage people to use catchall exceptions. The penalty of making people write "(KeyboardInterrupt, NameError, AttributeError)" when they want this will be beneficial by a) not further complicating the exception hierarchy, b) not encouraging catchalls, and c) making the developer decide what they want to ignore and what they want to propagate for borderline cases like TypeError.

On Fri, Apr 8, 2011 at 10:34 AM, Mike Graham <mikegraham@gmail.com> wrote:
I'm sorry, but I'm not quite following what you are saying here, perhaps due to the double negatives you seem to be using. (Or perhaps because my brain is not working 100% this morning. :-) Could you elaborate, perhaps with a few examples of good practice, bad practice, how each would be written with and without the hypothetical "BadCodeError" exception, and which forms you'd consider good and bad style? I could also see how combining this with the 'if' subclause might actually be beneficial: try: <something highly experimental> except Exception as e if not isinstance(e, BadCodeError): <apparently there was some bad data> Of course in reality bad data will often lead to a BadCodeError, the most common example being trying to call a method on a value that is unexpectedly None. Maybe getattr(None, anything) should raise some other exception? (Clearly just thinking aloud here. :-) -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 8, 2011 at 1:52 PM, Guido van Rossum <guido@python.org> wrote:
I'm pretty sure it's me!
I imagine common usage would be try: website = open_and_parse(url) except Exception as e if not isinstance(e, BadCodeError): print("Error downloading %s." % url) because I currently see tons of try: website = open_and_parse(url) except: print("Error downloading %s." % url) The right thing to do would likely be to figure out which errors mean you couldn't download the website and catch those. Here you might accidentally squash exceptions raised in parsing and issue the wrong error message. A more appropriate usage might be def my_event_loop(self): while True: try: self.advance() except BaseException as e: self.logger.exception() if isinstance(e, (NameError, AttributeError, KeyboardInterrupt, SystemExit, MemoryError)): raise Catchall exceptions (even of a slightly more limited nature) should be a rarity and typically is caused by bugs. Since this is something that should seldom be done and since there are borderline cases, I don't think that a language-level change helps. This list sort of suggests to me that I'm thinking more of DoNotCatchThisError, which includes BadCodeError and SomeExternalThingICannotHelpError.
I imagine this would be by far the most common use of BadCodeError, which suggests to me that if we had something like this, it would be important to have (a better-named GoodCodeError) or CatchableError.
Mike

On Fri, Apr 8, 2011 at 11:22 AM, Mike Graham <mikegraham@gmail.com> wrote:
Gotcha. I would actually say this is somewhat special because urlopen can raise so many different errors; thatr's why I called it out as an example earlier. If all errors raisable by urlopen due to external circumstances would derive from some common base exception, people would catch that exception. I agree that the "except Exception as e if not isinstance(BadCodeError):" idiom is not great, and given that it is such a mouthful, people who are currently writing "except:" are unlikely to change their ways.
You seem to be missing the reason why we introduced BaseException here... Read PEP 352.
But honestly that wasn't my intended use case. I was thinking more along the lines of trying to catch OSError with a specific errno value: except OSError as e if e.errno == errno.ENOTOBACCO: logging.info('Pipe has no tobacco')
I don't think we can define "negative" exception classes.
But it's too late for that -- BadCodeError must derive from Exception, and most 3rd party code also defines exceptions deriving from Exception. -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 8, 2011 at 2:32 PM, Guido van Rossum <guido@python.org> wrote:
I think I get what BaseException was conceived for, but it usually proves unsuitable for my usage. If I just do "except Exception" in this code (is that the idea?), it doesn't behave like I'd want: a) If my program exits due to a KeyboardInterrupt, it doesn't make it into my log, which I may want. b) If my program exits due to a loose GeneratorExit, it doesn't make it into my log, which I almost certainly want, and c) Plenty of Exception subclasses (like MemoryError and any sort of candidate for BadCodeError) are things that should be treated as uncatchable as often as the exceptions which don't subclass Exception. Perhaps I'm letting this cloud my vision of why you want BadCodeError.
I'm confused how that relates to BadeCodeError?
I don't think we can define "negative" exception classes.
class NotBadCodeError(Exception): __metaclass__ = abc.ABCMeta @classmethod def __subclasshook__(self, potential_subclass): if not issubclass(potential_subclass, BadCodeError): return True return False DON'T JUDGE ME!
Mike

On Fri, Apr 8, 2011 at 10:34 AM, Mike Graham <mikegraham@gmail.com> wrote:
I'm sorry, but I'm not quite following what you are saying here, perhaps due to the double negatives you seem to be using. (Or perhaps because my brain is not working 100% this morning. :-) Could you elaborate, perhaps with a few examples of good practice, bad practice, how each would be written with and without the hypothetical "BadCodeError" exception, and which forms you'd consider good and bad style? I could also see how combining this with the 'if' subclause might actually be beneficial: try: <something highly experimental> except Exception as e if not isinstance(e, BadCodeError): <apparently there was some bad data> Of course in reality bad data will often lead to a BadCodeError, the most common example being trying to call a method on a value that is unexpectedly None. Maybe getattr(None, anything) should raise some other exception? (Clearly just thinking aloud here. :-) -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 8, 2011 at 1:52 PM, Guido van Rossum <guido@python.org> wrote:
I'm pretty sure it's me!
I imagine common usage would be try: website = open_and_parse(url) except Exception as e if not isinstance(e, BadCodeError): print("Error downloading %s." % url) because I currently see tons of try: website = open_and_parse(url) except: print("Error downloading %s." % url) The right thing to do would likely be to figure out which errors mean you couldn't download the website and catch those. Here you might accidentally squash exceptions raised in parsing and issue the wrong error message. A more appropriate usage might be def my_event_loop(self): while True: try: self.advance() except BaseException as e: self.logger.exception() if isinstance(e, (NameError, AttributeError, KeyboardInterrupt, SystemExit, MemoryError)): raise Catchall exceptions (even of a slightly more limited nature) should be a rarity and typically is caused by bugs. Since this is something that should seldom be done and since there are borderline cases, I don't think that a language-level change helps. This list sort of suggests to me that I'm thinking more of DoNotCatchThisError, which includes BadCodeError and SomeExternalThingICannotHelpError.
I imagine this would be by far the most common use of BadCodeError, which suggests to me that if we had something like this, it would be important to have (a better-named GoodCodeError) or CatchableError.
Mike

On Fri, Apr 8, 2011 at 11:22 AM, Mike Graham <mikegraham@gmail.com> wrote:
Gotcha. I would actually say this is somewhat special because urlopen can raise so many different errors; thatr's why I called it out as an example earlier. If all errors raisable by urlopen due to external circumstances would derive from some common base exception, people would catch that exception. I agree that the "except Exception as e if not isinstance(BadCodeError):" idiom is not great, and given that it is such a mouthful, people who are currently writing "except:" are unlikely to change their ways.
You seem to be missing the reason why we introduced BaseException here... Read PEP 352.
But honestly that wasn't my intended use case. I was thinking more along the lines of trying to catch OSError with a specific errno value: except OSError as e if e.errno == errno.ENOTOBACCO: logging.info('Pipe has no tobacco')
I don't think we can define "negative" exception classes.
But it's too late for that -- BadCodeError must derive from Exception, and most 3rd party code also defines exceptions deriving from Exception. -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 8, 2011 at 2:32 PM, Guido van Rossum <guido@python.org> wrote:
I think I get what BaseException was conceived for, but it usually proves unsuitable for my usage. If I just do "except Exception" in this code (is that the idea?), it doesn't behave like I'd want: a) If my program exits due to a KeyboardInterrupt, it doesn't make it into my log, which I may want. b) If my program exits due to a loose GeneratorExit, it doesn't make it into my log, which I almost certainly want, and c) Plenty of Exception subclasses (like MemoryError and any sort of candidate for BadCodeError) are things that should be treated as uncatchable as often as the exceptions which don't subclass Exception. Perhaps I'm letting this cloud my vision of why you want BadCodeError.
I'm confused how that relates to BadeCodeError?
I don't think we can define "negative" exception classes.
class NotBadCodeError(Exception): __metaclass__ = abc.ABCMeta @classmethod def __subclasshook__(self, potential_subclass): if not issubclass(potential_subclass, BadCodeError): return True return False DON'T JUDGE ME!
Mike
participants (3)
-
Antoine Pitrou
-
Guido van Rossum
-
Mike Graham