7

Should we always enclose every function we write with a try...except block? I ask this because sometimes in one function we raise Exception, and the caller that calls this function doesn't have exception

def caller():
   stdout, stderr = callee(....)


def callee():
   ....
    if stderr:
       raise StandardError(....)

then our application crash. In this obvious case, I am tempted to enclose callee and caller with try..except.

But I've read so many Python code and they don't do these try..block all the time.


def cmd(cmdl):
    try:
        pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
    except Exception, e:
        raise e
    stdout, stderr = pid.communicate()
    if pid.returncode != 0:
        raise StandardError(stderr)
    return (stdout, stderr)

def addandremove(*args,**kwargs):
    target = kwargs.get('local', os.getcwd())
    f = kwargs.get('file', None)
    vcs = kwargs.get('vcs', 'hg')

    if vcs is "hg":
        try:
            stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
        except StandardError, e:
            // do some recovery
        except Exception, e:
            // do something meaningful
    return True

The real thing that bothers me is this:

If there is a 3rd function that calls addandremove() in one of the statements, do we also surround the call with a try..except block? What if this 3rd function has 3 lines, and each function calls itself has a try-except? I am sorry for building this up. But this is the sort of problem I don't get.

1
  • You should only put try...except blocks where you expect an error to occur, and only catch the specific type of exception you expect. Commented Jul 13, 2012 at 21:22

6 Answers 6

14

Exceptions are, as the name implies, for exceptional circumstances - things that shouldn't really happen

..and because they probably shouldn't happen, for the most part, you can ignore them. This is a good thing.

There are times where you do except an specific exception, for example if I do:

urllib2.urlopen("http://example.com")

In this case, it's reasonable to expect the "cannot contact server" error, so you might do:

try:
    urllib2.urlopen("http://example.com")
except urllib2.URLError:
    # code to handle the error, maybe retry the server,
    # report the error in a helpful way to the user etc

However it would be futile to try and catch every possible error - there's an inenumerable amount of things that could potentially go wrong.. As a strange example, what if a module modifies urllib2 and removes the urlopen attribute - there's no sane reason to expect that NameError, and no sane way you could handle such an error, therefore you just let the exception propagate up

Having your code exit with a traceback is a good thing - it allows you to easily see where the problem originate, and what caused it (based on the exception and it's message), and fix the cause of the problem, or handle the exception in the correct location...

In short, handle exceptions only if you can do something useful with them. If not, trying to handle all the countless possible errors will only make your code buggier and harder to fix


In the example you provide, the try/except blocks do nothing - they just reraise the exception, so it's identical to the much tidier:

def cmd(cmdl):
    pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
    stdout, stderr = pid.communicate()
    if pid.returncode != 0:
        raise StandardError(stderr)
    return (stdout, stderr)

# Note: Better to use actual args instead of * and **,
# gives better error handling and docs from help()
def addandremove(fname, local = None, vcs = 'hg'):
    if target is None:
       target = os.getcwd() 

    if vcs is "hg":
        stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
    return True

About the only exception-handling related thing I might expect is to handle if the 'hg' command isn't found, the resulting exception isn't particularly descriptive. So for a library, I'd do something like:

class CommandNotFound(Exception): pass

def cmd(cmdl):
    try:
        pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
    except OSError, e:
        if e.errno == 2:
            raise CommandNotFound("The command %r could not be found" % cmdl)
        else:
            # Unexpected error-number in OSError,
            # so a bare "raise" statement will reraise the error
            raise

    stdout, stderr = pid.communicate()
    if pid.returncode != 0:
        raise StandardError(stderr)
    return (stdout, stderr)

This just wraps the potentially confusing "OSError" exception in the clearer "CommandNotFound".

Rereading the question, I suspect you might be misunderstanding something about how Python exceptions work (the "and the caller that calls this function doesn't have exception" bit, so to be hopefully clarify:

The caller function does not need any knowledge of the exceptions that might be raised from the children function. You can just call the cmd() function and hope it works fine.

Say your code is in a mystuff module, and someone else wants to use it, they might do:

import mystuff

mystuff.addandremove("myfile.txt")

Or, maybe they want to give a nice error message and exit if the user doesn't have hg installed:

import mystuff

try:
    mystuff.addandremove("myfile.txt")
except mystuff.CommandNotFound:
    print "You don't appear to have the 'hg' command installed"
    print "You can install it with by... etc..."
    myprogram.quit("blahblahblah")
Sign up to request clarification or add additional context in comments.

6 Comments

Great answer. (+1), although depending on how you imported urllib2 (import urllib2 instead of from urllib2 import *), you would get an AttributeError instead of a NameError :-p
@mgilson oops, sleepyness + typo in REPL made by overcorrect myself and write NameError instead (import urllib; urllib2.blah), although.. that kind of confirms my point.. I mean, I wrote that to confirm my point. Yes...
The EAFP principle makes it so exceptions aren't necessarily "things that shouldn't really happen."
@dbr Thanks! I took all the suggestions, is that what I am supposed to do? Please look at my updated code.
@dbr Thanks. So the part with "not re-raising" is clear to me. But the ultimate question is still unclear to me. I've stated that in my edit. Sorry for bothering, but really appreciate your great help!
|
6

You should use a try catch block so that you can specifically locate the source of the exception. You can put these blocks around anything you want, but unless they produce some sort of useful information, there is no need to add them.

1 Comment

+1. You put the try...except at the location where you perform error recovery. If you have no error recovery then yes, your application crashes when there is a problem. By definition, really.
6

try/except clauses are really only useful if you know how to handle the error that gets raised. Take the following program:

while True:
    n=raw_input("Input a number>")
    try:
       n=float(n)
       break
    except ValueError:
       print ("That wasn't a number!")  #Try again.

However, you may have a function like:

def mult_2_numbers(x,y):
    return x*y

and the user may try to use it as:

my_new_list=mult_2_numbers([7,3],[8,7])

The user could put this in a try/except block, but then my_new_list wouldn't be defined and would probably just raise an exception later (likely a NameError). In that case, you'd make it harder to debug because the line number/information in the traceback is pointing to a piece of code which isn't the real problem.

7 Comments

Exactly! That's the problem. When I put the master inside the try..except, I am pointed to the exception line of the master function, and then I would have no idea what happened, and would spend an hour tyring to debug. But then again, if all 10 lines can cause potential problems... what do you do? What I mean is that testings can eliminate a lot of the errors, but for a web application, one function can call 10 different functions to construct a response object. But when it returns, one of the attributes is empty, and the master cannot use this incomplete response object.
@CppLearner -- Then it's up to the programmer. If there's a sensible action to take when an incomplete response is recieved, then a try/except makes sense (e.g. try to get the response again, or skip this response, etc.). of course, if you have control over why the response was bad, you may want to at least print a warning before trying again so you know there's a problem ... If there's no way to recover from this error, it's best to not have a try/except so you can track down the reason why you got a bad response in the first place.
thanks for the suggestion. Do you mind to look at the updated code. Am I still doing the wrong thing?
@CppLearner -- There isn't really a point to doing try: something(); except Exception as e: raise e. You're catching the exception and re-raising it. Ultimately, the caller sees the same thing. If you catch an exception, you should do something. you can do something with the exception (e.g. print (e)), or you can just take some other action -- e.g. try to run the command again or print that the command was poorly formed, print the string and exit, ... But if you catch an exception, you should do more than just re-raise it.
Right. For addandremove I should do this if applicable: except StandardError: //do something more than just raising. Then again, this becomes really really confusing to me. If there is a 3rd function that calls addandremove() in one of the statements, do we also surround the call with a try..except block? What if this 3rd function has 3 lines, and each function calls itself has a try-except? I am sorry for building this up. But this is the sort of problem I don't get.
|
1

There are a couple of programming decisions for your coding team to make regarding introspection type tools and "exception" handling.

One good place to use exception handling is with Operating System calls such as file operations. Reasoning is, for example, a file may have it's access restricted from being accessed by the client appliction. That access restriction is typically an OS-admin task, not the Python application function. So exceptions would be a good use where you application does NOT have control.

You can mutate the previous paragraph's application of exceptions to be broader, in the sense of using Exceptions for things outside the control of what your team codes, to such things as all "devices" or OS resources like OS timers, symbolic links, network connections, etc.

Another common use case is when you use a library or package that is -designed- to through lots of exceptions, and that package expects you to catch or code for that. Some packages are designed to throw as few exceptions as possible and expect you to code based on return values. Then your exceptions should be rare.

Some coding teams use exceptions as a way to log fringe cases of "events" within your application.

I find when desiding whether to use exceptions I am either programing to minimize failure by not having a lot of try/except and have calling routines expect either valid return values or expect an invalid return values. OR I program for failure. That is to say, I program for expected failure by functions, which I use alot of try/except blocks. Think of programming for "expected" failure like working with TCP; the packets aren't garenteed to get there or even in order, but there are exception handling with TCP by use of send/read retrys and such.

Personally I use try-except blocks around the smallest possible block sizes, usually one line of code.

Comments

0

It's up to you; the main exceptions' roles involve (a quote from this splendid book):

  • Error handling
  • Event notification
  • Special-case handling
  • Termination actions
  • Unusual control flows

Comments

0

When you know what the error will be, use try/except for debugging purpose. Otherwise, you don't have to use try/except for every function.

2 Comments

How does using try/except aid in debugging? The traceback of the unhandled exception will give you all the information you could print by handling it ...
I believe it might make sense to use an intermediate try/catch to add additional data to the trace, in the form of chained exceptions. So if you not only want to know the source location of the problem, but the relevant data item as well, this might be useful. If that is the intention of this answer, an edit providing more detail would be welcome.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.