In this article I will introduce you to exception handling. In the previous articles in this series we use exception handling to cover some rare exception cases (remember the Guess the Number game?) and now it is time to explain how to do it in your own application — and when to do it.
What are exceptions?
Exceptions are errors which happen during the execution of a script / application. As the name implies: exceptions occur seldom, they are an exception to the rule of general workflow.
In Python exceptions are called errors to make developers migrating from other languages confuse. The base error class is Exception, other exceptions are a subclass of this one — and this makes things more confusing.
Every time an exception is raised it goes up the call-stack until a code block handles it. If no code is there to handle the exception, the interpreter takes over, writes out a nasty error message and terminates the running script.
Naturally in an error handling block you can raise a new exception or throw the currently further to the top. This is a common practice in web applications where you want to log that a service call resulted in an exception but you want to notify the user through the user interface too that there was an error. And for this you have to forward the exception.
Catching an exception
As you can remember, in the Guess the Number game we already used exception handling. A general rule to handle exceptions is that you need a try-except block:
try: block of code which might raise an error except: exception handling
As you can see, the code which may raise an exception goes into a try block. The error handling then happens in the except block.
However using plain except: as in the example above is considered a bad practice in most of the cases. That’s because with this you handle all the exceptions in that given block and sometimes this is more than you want.
Let’s create a simple example where we call a function which always raises an exception:
def error_function(): return int('nine')
If we define this function in the interactive interpreter and call it, we get an exception:
>>> error_function() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in error_function ValueError: invalid literal for int() with base 10: 'nine'
Now we wrap this function call into a try-except block:
>>> try: ... error_function() ... except: ... print('an exception occurred') ...
an exception occurred
In the example above we catch all exceptions which may be raised in the try block. However as I mentioned it this is a bad practice. It is better to catch those exceptions we know they may come and let really unexpected exceptions to go up until they are handled. For this we will catch only syntax errors in the beginning:
>>> try: ... error_function() ... except SyntaxError: ... print('an exception occurred') ... Traceback (most recent call last): File "<stdin>", line 2, in <module> File "<stdin>", line 2, in error_function ValueError: invalid literal for int() with base 10: 'nine'
As you can see in this case the exception is handled by the interpreter. So change the exception handling to handle ValueErrors:
>>> try: ... error_function() ... except ValueError: ... print('an exception occurred') ...
an exception occurred
Writing out the exception
Sometimes it is not enough to catch the exception but you have to log the cause of the error (sometimes into a log file or to the console) to let the users or application administrators know what is happening.
To do this you can write the previous exception handling block as follows:
>>> try: ... error_function() ... except ValueError as e: ... print('an exception occurred:', e) ...
an exception occurred: invalid literal for int() with base 10: ‘nine’
As you can see in the example above you get hold of the exception object itself and you can write it to the console. If you do this the message of the exception is displayed.
Raising an exception
I was talking about raising exceptions. If you have been patient and didn’t google up how to do this then the time has come. Well, it is the same as I refer to it:
This raises a general exception without much about it. You can try it out in the interactive interpreter:
>>> raise Exception Traceback (most recent call last): File "<stdin>", line 1, in <module> Exception >>> raise Exception("MY custom error") Traceback (most recent call last): File "<stdin>", line 1, in <module> Exception: MY custom error
As you can see, you can provide a message as a constructor argument to the exception and this message is displayed when the interpreter handles the error — or you can extract it as we have seen in the previous section.
Sometimes exceptions occur. In some cases you either want to handle them sometimes you just want the script / application to stop executing. In this article we looked at the ways how to catch exceptions and handle them and how to get the root cause printed to the user.