In software development, different types of errors can occur. They could be syntax errors, logical errors, or runtime errors.
Syntax errors most probably occur during the initial development phase and are a result of incorrect syntax. Syntax errors can be caught easily when the program is compiled for execution.
Logical errors, on the other hand, are a result of improper logical implementation. An example would be a program accessing a unsorted list assuming it to be sorted. Logical errors are the most difficult ones to track.
Runtime errors are the most interesting errors which occur, if we don't consider all the corner cases. An example would be trying to access a non-existent file.
In this tutorial, we'll learn how to handle errors in Python and how to log the errors for a better understanding of what went wrong inside the application.
Handling Exceptions in Python
Let's start with a simple program to add two numbers in Python. Our program takes in two parameters as input and prints the sum.
Here is a Python program to add two numbers:
def addNumbers(a, b): print a + b addNumbers(5, 10)
Try running the above Python program, and you should have the sum printed.
While writing the above program, we didn't really consider the fact that anything can go wrong. What if one of the parameters passed is not a number?
We haven't handled that case, hence our program would crash with the following error message:
Traceback (most recent call last): File "addNumber.py", line 4, in <module> addNumbers('', 10) File "addNumber.py", line 2, in addNumbers print a + b TypeError: cannot concatenate 'str' and 'int' objects
We can handle the above issue by checking if the parameters passed are integers. But that won't solve the issue. What if the code breaks down due to some other reason and causes the program to crash? Working with a program which crashes on being encountered with an error is not a good sight. Even if an unknown error is encountered, the code should be robust enough to handle the crash gracefully and let the user know that something is wrong.
Handling Exceptions Using Try and Except
In Python, we use the
except statements to handle exceptions. Whenever the code breaks down, an exception is thrown without crashing the program. Let's modify the add number program to include the
def addNumbers(a, b): try: return a + b except Exception as e: return 'Error occurred : ' + str(e) print addNumbers('', 10)
Python would process all code inside the
except statement. When it encounters an error, the control is passed to the
except block, skipping the code in between.
As seen in the above code, we have moved our code inside a
except statement. Try running the program and it should throw an error message instead of crashing the program. The reason for the exception is also returned as an exception message.
The above method handles unexpected exceptions. Let's have a look at how to handle an expected exception. Assume that we are trying to read a particular file using our Python program, but the file doesn't exist. In this case, we'll handle the exception and let the user know that the file doesn't exist when it happens. Have a look at the file reading code:
try: try: with open('fname') as f: content = f.readlines() except IOError as e: print str(e) except Exception as e: print str(e)
In the above code, we have handled the file reading inside an
IOError exception handler. If the code breaks down due to unavailability of the file
fname, the error would be handled inside the
Similar to the
IOError exceptions, there are a lot more standard exceptions like
ImportError, to name a few.
We can handle multiple exceptions at a time by clubbing the standard exceptions as shown:
try: with open('fname') as f: content = f.readlines() printb except (IOError,NameError) as e: print str(e)
The above code would raise both the
NameError exceptions when the program is executed.
Assume that we are using certain resources in our Python program. During the execution of the program, it encountered an error and only got executed halfway. In this case, the resource would be unnecessarily held up. We can clean up such resources using the
finally clause. Take a look at the below code:
try: filePointer = open('fname','r') try: content = filePointer.readline() finally: filePointer.close() except IOError as e: print str(e)
If, during the execution of the above code, an exception is raised while reading the file, the
filePointer would be closed in the
Logging in Python
When something goes wrong inside an application, it becomes easier to debug if we know the source of the error. When an exception is raised, we can log the required information to track down the issue. Python provides a simple and powerful logging library. Let's have a look at how to use logging in Python.
import logging # initialize the log settings logging.basicConfig(filename='app.log',level=logging.INFO) try: logging.info('Trying to open the file') filePointer = open('appFile','r') try: logging.info('Trying to read the file content') content = filePointer.readline() finally: filePointer.close() except IOError as e: logging.error('Error occurred ' + str(e))
As seen in the above code, first we need to import the logging Python library and then initialize the logger with the log file name and logging level. There are five logging levels: DEBUG, INFO, WARNING, ERROR, and CRITICAL. Here we have the set the logging level to INFO, hence the INFO and above logs would be logged.
Getting the Stack Trace
In the above code we had a single program file, hence it was easier to figure out where the error had occurred. But what do we do when multiple program files are involved? In such a case, getting the stack trace of the error helps in finding the source of the error. The stack trace of the exception can be logged as shown:
import logging # initialize the log settings logging.basicConfig(filename = 'app.log', level = logging.INFO) try: filePointer = open('appFile','r') try: content = filePointer.readline() finally: filePointer.close() except IOError as e: logging.exception(str(e))
If you try to run the above program, on raising an exception the following error would be logged in the log file:
ERROR:root:[Errno 2] No such file or directory: 'appFile' Traceback (most recent call last): File "readFile.py", line 7, in <module> filePointer = open('appFile','r') IOError: [Errno 2] No such file or directory: 'appFile'
Wrapping It Up
In this tutorial, we saw how to get started with handling errors in Python and using the logging module to log errors. We saw the usage of
finally statements, which are quite useful when dealing with error handling in Python. For more detailed information, I would recommend reading the official documentation on logging. Also have a look at the documentation for handling exceptions in Python.
Do let us know your thoughts in the comments below.