Catching Python Exceptions – The tryexceptelse keywords

Often times when coding a python masterpiece, there are certain things that could go wrong when executing your masterfully designed code. Things such as files or directories that are missing, empty strings, variables that are supposed to be strings but are actually arrayed at run-time.

These things are called exceptions in Python. This is what the try keyword is for.

It allows the execution of potentially breaking code that is nested in a comfortable block. This block will attempt to catch any of those nasty exceptions, and then execute code to either recover from the error or notify the user of the breakage.

For example, this function would like to run:

ef succeed(yn):
    if yn:
        return True
    else:
        raise Exception("I can't succeed!")

But, as we can tell, it has the potential to raise an exception. When an exception is raised, and it’s not in a try block with the proper exception handling, it will stop the execution of the code.

If this code is run as:

>>> succeed(True)
True

It will be fine and no one will know the difference.

But, if it’s run as

>>> succeed(False)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<stdin>", line 5, in succeed
Exception: I can't succeed!

We get this lovely message about the code not being able to succeed. Except that it’s not that lovely. It’s actually pretty ugly.

So what can we do?

That’s where the try block comes in!

So, to make our above code run-time friendly, we can do this:

from random import randint
 
def handle_exception(e):
    print(e)
    print('But I can be safe!')
 
try:
    succeed(randint(False, True))
except Exception as e:
    handle_exception(e)

What did we just do? Well, randint will choose a random integer between the two given inputs. In this case, False is 0 and True is 1. So the succeed function will randomly raise an exception.

Now with the except part. We tell Python to execute handle_exception only if the succeed function raises an exception.

So if we run this code, the output will either be nothing, if we succeed, and if we don’t:

I can't succeed!
But I can be safe!

But what if you want to execute a piece of code after a success? You could do this

def another_method_that_could_fail():
    fail = randint(False, True)
 
    if fail:
        raise RuntimeError('I definitely failed.')
    else:
        print("Yay! I didn't fail!")
 
try:
    succeed(randint(False,True))
    another_method_that_could_fail()
except Exception as e:
    handle_exception(e)

Now, we see that if succeed doesn’t raise an exception, then another_method_that_could_fail runs! Amazing! We did it!

But wait! If another_method_that_could_fail runs, it will run handle_exception again, and we want to print a different message. Darn.

So what do we do? Well, we could add another piece to our try block like so:

def handle_runtime(re):
    pass
 
try:
    succeed(randint(False, True))
    another_method_that_could_fail()
except RuntimeError as re:
    handle_runtime(re)
except Exception as e:
    handle_exception(e)

Okay, that’s all well and good. But now, how do we run a piece of code on a successful run, with no exceptions? Well, there is a part of the try block that is not well known. It’s the the else keyword. Observe:

If we take our previous code, and modify it a bit:

try:
    succeed(randint(False, True))
    another_method_that_could_fail()
except RuntimeError as re:
    handle_runtime(re)
except Exception as e:
    handle_exception(e)
else:
    print('Yes! No exceptions this time!')

Then we get a nice message printed only when we succeed. Okay, but what if we need a piece of code to run no matter what? Useful for, say, closing a file that has been opened in the try block? Yes!

From stage left, enter the finally keyword. It will do exactly what we want.

Observe it in glorious action:

try:
    succeed(randint(False, True))
    another_method_that_could_fail()
except RuntimeError as re:
    handle_runtime(re)
except Exception as e:
    handle_exception(e)
else:
    print('Unknown error occurred. Exiting.')
    exit(2)
finally:
    print("Finally! I'm done. I don't care if I failed or not. I'm DONE.")

And Voilà! There we have it. Now we will always print the message in the finally section.

That’s it for the try/except/else/finally block. I hope I educated you well in your quest to become a Python master.

Sayōnara, for now.

Leave a Reply

Your email address will not be published. Required fields are marked *