[Tutor] more info about exceptions

D-Man dsh8290@rit.edu
Fri, 19 Jan 2001 10:38:37 -0500


On Tue, Jan 16, 2001 at 07:49:40PM -0800, Sheila King wrote:
[snip]
| Good tips. Thanks.
| 
| I have a question, about try/except blocks in general.
| (I am assuming they come in pairs, like if/else, right?)
| 
| If I put
| 
| try:
|     (body of my script)
| 
| and suppose the script tries to connect to the SMTP server, but fails, will it
| try more than once? Or it tries once, then gives up, and raises an exception?
| How many times does "try" try until it gives up? Once? Several times? If I had
| some code that was supposed to execute, like it needed to connect to some
| server, and for some reason that server was not available for connection, so
| it raised an error, how would I keep the script running and trying to connect
| to that server later?
| 

I think this part was already answered, but I'm a bit behind in
replying ;-).

The "try" block simply means that the following block of statements
might fail.  If it does fail, then below it, in "except" blocks, you
have specified the error handling that should be done.  It will only
try once unless you put a loop around it that specifies to repeat the
operation.



[ I recall you asking about how exceptions are used, but I can't find
the right message to reply to,  answer follows ]

Suppose you have 2 functions, func1 and func2, that might fail.  You
call func1, and func1 calls func2 to do the work.  In a C-ish system
(ie no exceptions) you would have to write code like this:

# some new "keywords" to improve readability
true = 1 
false = 0

success = func1()

if not success :
        print "There was an error"
        # now check the global /int/ to get an idea of what the error
        # was
        perror()   # (this is part of the std C lib, but can't produce
                   # REALLY useful messages)
        return false

# now continue with your work
return true

####################
def func1() :
        success = func2()
        if not success :
                return false
        # continue with operations



Also, if you have several steps to perform that depend on the previous
function succeeding, or you have different error handling to do, you
can easily end up with several layers of nested if blocks that are
ugly, hard to read, and obscure the purpose of your function.  Each
function must be sure to return success/failure and to check the
return of all functions that are called.


If you have a Pythonic system (ie you have exceptions) you can instead
write stuff like this:

try:
        func1()
        # some other operations
except:
        print "A useful and informative error message"


and func1 could be much simpler 

def func1() :
        func2() # func2 will throw an exception if it fails,
                # in that case, func1's operation will immediately
                # cease and control will be passed up to the
                # try/except block that will catch the exception


With exceptions, when you are at a deep level of nesting where you
don't want to actually handle the exception you can just ignore it and
the higher level try/except block will take care of it.  (think of a
GUI app where opening a file fails,  you want the GUI part not the
file io part to display an error) 

Another benefit of exceptions is the following code (I have more
practice with the Java std. lib's exceptions at the moment so I'll use
those but the idea is the same)


try :
        file = open( "somefilename.text" , "r" )
        data = file.readline()
        do_something_with_data( data )
except IOException , e :
        print "There was an error reading the file:"
        print e.getMessage()
except DataError , e :
        # you might define do_something_with_data so that it throws a
        # DataError exception if the data isn't good
        print "The data from the file was corrupt."
        print e.getMessage()


The point here is that since an exception is an object it can contain
state.  The exception's state usually contains a message for the user
(e.getMessage()) and can also hold information about what the exact
problem was (something that C's errno variable can't do).  You can
also handle different types of errors in separate blocks and the
syntax makes it quite simple.


I posted a longer and much more detailed explanation on python-list a
while back.  Check the archives under the subject "Are exceptions
really used in practice" (or something similar).

HTH,
-D