CppLearner
CppLearner

Reputation: 17040

Do we use try,except in every single function?

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.

Upvotes: 7

Views: 14366

Answers (6)

dbr
dbr

Reputation: 169563

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")

Upvotes: 14

DevPlayer
DevPlayer

Reputation: 5579

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.

Upvotes: 1

user1524686
user1524686

Reputation: 1

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.

Upvotes: 0

Vidul
Vidul

Reputation: 10538

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

Upvotes: 0

mgilson
mgilson

Reputation: 309919

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.

Upvotes: 6

Rob Wagner
Rob Wagner

Reputation: 4421

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.

Upvotes: 6

Related Questions