7

When I work with handling exceptions, I notice that I often have to deal with the ones I had no idea about. Especially it is noticeable when I program a method that grabs data from web. An error may occur, for example, due to connection loss, I can handle it. But then another error occurs, a different error with the same cause - connection loss. Okay, added it. Sometimes even yet another occurs. So, the problem is, I am never sure enough if I've handled all the possible errors that may occur due to a certain cause.

At first, I thought about going wildcard and do something like (example in Python):

try:
    #do stuff
except:
    #handle error

But it proved to be a wrong approach soon, cause if I need to handle, say, KeyboardInterrupt, which is raised when a user terminates the program, instead of being handled by a scope I want it to be handled by, it is handled by this wildcard, which is not supposed to have anything to do with it.

So how do I handle exceptions I don't know of but that may possibly occur (or not occur)? Some kind of exceptEverythingBut KeyboardInterrupt:? I doubt many languages have that in their syntax.

EDIT1: a really simplified example:

#!/usr/bin/python3 -u
# -*- coding: utf-8 -*-

try:
    while True:
        try:
            print(1)
        except:
            print(2)
except KeyboardInterrupt:
    print('end')

When I press Ctrl+C, I want it to print 'end' and finish. But instead it prints 2 and continues execution.

If I try this:

#!/usr/bin/python3 -u
# -*- coding: utf-8 -*-

try:
    while True:
        try:
            print(1)
        except KeyboardInterrupt:
            break
        except:
            print(2)
except KeyboardInterrupt:
    print('end')

it finishes, but it skips the outer except and doesn't print 'end'. And that's not what I want. So, the only way I see is to prevent the inner scope from handling KeyboardInterrupt altogether. But it is not possible if there is a except: or except KeyboardInterrupt: in there. So, I need to specify exactly which errors I want to handle in the inner except. But, as I mentioned in the beginning, I don't always know what they can be.

I'm asking this question, because my common way to do it is to just let the program unexpectedly fail several times, read the logs and add handling of errors I didn't know about to new versions; however this could just be a naïve approach, so I want to know how it is done by experienced people.

4
  • 1
    See stackoverflow.com/questions/4990718/… for a previous discussion. Jan 27, 2016 at 12:10
  • Check the documentation and hope it lists all relevant errors. Java has checked exceptions as a language feature to avoid this problem (but that decision leads to other problems) Jan 27, 2016 at 13:06
  • Personally I think that expected errors should be returned as some kind of tagged union/maybe type instead of being thrown as exception and the language should offer something like Rust's try! macro as syntax sugar. Jan 27, 2016 at 13:12
  • 1
    What are you doing when you catch these errors? Jan 28, 2016 at 23:54

4 Answers 4

7

Just rethrow the exception when you get it:

try:
  #do stuff
except KeyboardInterrupt:
  raise
except:
  #do other stuff

You specify that you get an exception that you know that you want to handle differently than default case, but you don't know how to handle it - so you just pass it, by throwing it again.

Some working example:

#!/usr/bin/python2

if __name__ == "__main__":
    try:
        while True:
            try:
                print(1)
            except KeyboardInterrupt:
                raise
            except:
                print(2)
    except KeyboardInterrupt:
        print('end')

Please note that you shouldn't use except:, as this is a bad practice. except: used in this example is used in sole purpose to make things as simple as possible.

7
  • Yeah, this could be a better idea. Or I'll just rethrow KeyboardInterrupt again.
    – Highstaker
    Jan 27, 2016 at 13:37
  • @Highstaker That's what I'm doing. Sorry for misleading example, I was writing it from head and forgot to write second KeyboardInterrupt. Please check out edited version. Jan 27, 2016 at 14:06
  • No problem. That's how I did it. 'Cept I wrote raise KeyboardInterrupt instead of just raise
    – Highstaker
    Jan 27, 2016 at 14:10
  • 1
    @MatthewRock, @Highstaker: Please do not ever advocate the use of except: without a full weighing of the consequences. At least use except Exception:, but you should handle that with care as well. Jan 28, 2016 at 23:47
  • @SjoerdJobPostmus I never did. I wanted to show how to handle the case, not write a full working program. I am not proficient in the language OP's using. I wanted to show idea in the easiest way. Never said anything about except: being good or bad. Jan 29, 2016 at 1:26
10

In general, you should not use except:, except for a few extremely rare circumstances, most notably log-and-reraise, or at the very top-level after which the program must end. Do not use it in libraries, or any non-top-level method, at all.

Instead, please use except Exception:. It catches most 'normal' errors, except a few special ones (KeyboardInterupt, 'SystemExit`, and some others). See https://docs.python.org/3.6/library/exceptions.html#exception-hierarchy.

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- ... (all other exceptions)

```

In most cases, I would even recommend against using except Exception:, and instead chose the most specific exception type(s) possible. This is because things like NameError, AttributeError, KeyError and the like are all subclasses of Exception. You can hide bugs (typos) in a very subtle way with this.

When dealing with network connections, you're most likely to want to use something like except socket.error:, or except LibrarySpecificErrorHere. Check the exception hierarchy for the networking library you use. You don't necessarily have to use the leaf exception classes.

With a wider except clause, more bugs will be hidden by it. Therefore: have as little code as possible in the try block, and have the exception classes as specific as possible.

Consider this:

bars = []
for foo in foos:
    try:
        bar = foo.bar
    except AttributeError:
        # Sometimes we get a `foo` that does not have a `bar` set yet.
        pass
    else:
        bar.apend(bar)

versus

bars = []
for foo in foos:
    try:
        bar = foo.bar
        bars.apend(bar)
    except AttributeError:
        # Sometimes we get a `foo` that does not have a `bar` set yet.
        pass

Did you notice the spelling error in append (I 'accidentally' wrote apend). You are smart, so you did notice. But Python misses it in the second case, while in the first case the scope of the exception handling is smaller, and thus a bug was lurking outside the scope of the handling, got out, and you saw it.

The same goes for this piece of code:

bars = []
for foo in foos:
    try:
        bar = fo.bar
    except:
        # Sometimes we get a `foo` that does not have a `bar` set yet.
        pass
    else:
        bars.append(bar)

Oopsie, did I just accidentally ignore a NameError?

Again: except: catches all exceptions, even the ones you are not supposed to catch (SystemExit, KeyboardInterupt, ...). In general, the application is supposed to be dead after any of those exceptions are raised. Be very specific in handling exceptions, make sure as few bugs as possible get hidden by overly aggressive exception handling.

2
  • I agree, doing a bare except: can be dangerous. But, if I use except Exception:, will it catch a library-specific error, like urllib2.errors.connectionFellApart? (I know this particular one doesn't exist in reality, it's just a random example)
    – Highstaker
    Jan 29, 2016 at 4:03
  • 2
    @Highstaker: yes. It should. If not, raise a bug with the library development team. For code in the stdlib (which includes urllib2), you should definitly be fine. Also, check the errors in urllib2.errors to see if they have a common ancestor, and use that. Jan 29, 2016 at 5:59
0

If you assign a custom error handling function to sys.excepthook, it will be called whenever an exception is raised and you won't need to use a try-except statement. Something like this should work:

import sys

def on_error(etype, value, tb):
    if etype == KeyboardInterrupt:
        return
    # handle other types of errors

sys.excepthook = on_error
-4

And this is why I like Java - no such surprises there (every function explicitly tells you what Exceptions it might throw).

Wouldn't that be something like this?

try:
    #do stuff
except(KeyboardInterrupt):
    pass
else:
    #handle error
2
  • No, not really. I'll append my question with an example.
    – Highstaker
    Jan 27, 2016 at 12:22
  • 3
    Yeah, that's other common construct, but not the one you want to use here - pass means "ignore", raise means "rethrow". Jan 27, 2016 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.