Module 9
Debugging
Debugging is a process of identifying and removing errors from the program.
Python provides a few tools and techniques to identify what exactly your code is doing and where it’s going wrong.
9.1 Exceptions
An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.
The term exception is, in fact, short form for the phrase “exceptional event.”
Exception Handling
Exception handling is the process responding to the occurrence of exceptions. In Python, if we do not handle the exception, or when we get an error, the program crashes. For a real-world application, this is not desired behavior. Instead, a mechanism must be built to detect errors, handle them, and then continue to run.
A simple example is “divide-by-zero” error. Write the following code and save it as division_error.py
.
def divideBy(num):
return 21/num
print(divideBy(2))
print(divideBy(7))
print(divideBy(0))
print(divideBy(1))
The output of this code will be as follows:
11.5
3.0
Traceback (most recent call last):
File "C:/division_error.py", line 6, in <module>
print(divideBy(0))
File "C:/division_error.py", line 2, in divideBy
return 21 / num
ZeroDivisionError: division by zero
Here, a ZeroDivisionError happens as you tried to divide a number by zero.
try … except statement
In Python, errors such as above can be handled with try
and except
statements. The code that could potentially cause an error is put in try block and in case of an error we define what should be done in the except block.
Syntax:
try:
statement/s
except errorType:
statement/s
Example
def divideBy(num):
try:
return 21/num
except ZeroDivisionError:
print('Error: Invalid argument.')
print(divideBy(2))
print(divideBy(7))
print(divideBy(0))
print(divideBy(1))
Output
11.5
3.0
Error: Invalid argument.
None
21.0
List of Python built-in exceptions
Exception | Cause of Error |
---|---|
AssertionError | Raised when assert statement fails. |
AttributeError | Raised when attribute assignment or reference fails. |
EOFError | Raised when the input() functions hits end-of-file condition. |
FloatingPointError | Raised when a floating point operation fails. |
GeneratorExit | Raise when a generator's close() method is called. |
ImportError | Raised when the imported module is not found. |
IndexError | Raised when index of a sequence is out of range. |
KeyError | Raised when a key is not found in a dictionary. |
KeyboardInterrupt | Raised when the user hits interrupt key (Ctrl+c or delete). |
MemoryError | Raised when an operation runs out of memory. |
NameError | Raised when a variable is not found in local or global scope. |
NotImplementedError | Raised by abstract methods. |
OSError | Raised when system operation causes system related error. |
OverflowError | Raised when result of an arithmetic operation is too large to be represented. |
ReferenceError | Raised when a weak reference proxy is used to access a garbage collected referent. |
RuntimeError | Raised when an error does not fall under any other category. |
StopIteration | Raised by next() function to indicate that there is no further item to be returned by iterator. |
SyntaxError | Raised by parser when syntax error is encountered. |
IndentationError | Raised when there is incorrect indentation. |
TabError | Raised when indentation consists of inconsistent tabs and spaces. |
SystemError | Raised when interpreter detects internal error. |
SystemExit | Raised by sys.exit() function. |
TypeError | Raised when a function or operation is applied to an object of incorrect type. |
UnboundLocalError | Raised when a reference is made to a local variable in a function or method, but no value has been bound to that variable. |
UnicodeError | Raised when a Unicode-related encoding or decoding error occurs. |
UnicodeEncodeError | Raised when a Unicode-related error occurs during encoding. |
UnicodeDecodeError | Raised when a Unicode-related error occurs during decoding. |
UnicodeTranslateError | Raised when a Unicode-related error occurs during translating. |
ValueError | Raised when a function gets argument of correct type but improper value. |
ZeroDivisionError | Raised when second operand of division or modulo operation is zero. |
Raising Exceptions
We saw above that Python raises an exception whenever it tries to execute invalid code. But, we can also raise your own exceptions in your code.
Raising an exception is a way of saying, “Stop running the code in this function and move the program execution to the except
statement.”
Exceptions are raised with a raise
statement. In code, a raise
statement consists of the following:
-
The
raise
keyword -
A call to the
Exception()
function -
A string with a helpful error message passed to the
Exception()
function
Try the following in the interactive shell:
>>> raise Exception('This is the error message.')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
raise Exception('This is the error message.')
Exception: This is the error message.
If there are no try
and except
statements covering the raise
statement that raised the exception, the program simply crashes and displays the exception’s error message.
Often it’s the code that calls the function, not the function itself, that knows how to handle an exception. So you will commonly see a raise
statement inside a function and the try
and except
statements in the code calling the function.
For example, enter the following code and save the program as areaCalculator.py:
def calculateArea(width, height):
if width <= 2:
raise Exception('Width must be greater than 2.')
if height <= 2:
raise Exception('Height must be greater than 2.')
try:
calculateArea(2, 1)
except Exception as err:
print('An exception happened: ' + str(err))
This program uses the except Exception as err
form of the except
statement. If an Exception
object is returned from calculateArea()
, this except
statement will store it in a variable named err
. The Exception
object can then be converted to a string by passing it to str()
to produce a user-friendly error message.
Using the try
and except
statements, you can handle errors more gracefully instead of letting the entire program crash.
Getting the Traceback as a String
When Python encounters an error, it produces a treasure trove of error information called the traceback. The traceback includes the error message, the line number of the line that caused the error, and the sequence of the function calls that led to the error. This sequence of calls is called the call stack.
Open a new file editor window in IDLE, enter the following program, and save it as errorExample.py:
def spam():
bacon()
def bacon():
raise Exception('This is the error message.')
spam()
When you run errorExample.py, the output will look like this:
Traceback (most recent call last):
File "errorExample.py", line 7, in <module>
spam()
File "errorExample.py", line 2, in spam
bacon()
File "errorExample.py", line 5, in bacon
raise Exception('This is the error message.')
Exception: This is the error message.
From the traceback, you can see that the error happened on line 5, in the bacon()
function. This particular call to bacon()
came from line 2, in the spam()
function, which in turn was called on line 7. In programs where functions can be called from multiple places, the call stack can help you determine which call led to the error.
The traceback is displayed by Python whenever a raised exception goes unhandled. But you can also obtain it as a string by calling traceback.format_exc()
. This function is useful if you want the information from an exception’s traceback but also want an except
statement to gracefully handle the exception. You will need to import Python’s traceback
module before calling this function.
For example, instead of crashing your program right when an exception occurs, you can write the traceback information to a log file and keep your program running. You can look at the log file later, when you’re ready to debug your program. Enter the following into the interactive shell:
>>> import traceback
>>> try:
raise Exception('This is the error message.')
except:
errorFile = open('errorInfo.txt', 'w')
errorFile.write(traceback.format_exc())
errorFile.close()
print('The traceback info was written to errorInfo.txt.')
116
The traceback info was written to errorInfo.txt.
The 116
is the return value from the write()
method, since 116 characters were written to the file. The traceback text was written to errorInfo.txt.
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
Exception: This is the error message.