16  Error Handling

Sometimes our programs will encounter errors.

16.1 Handling Errors

In Python, we can use a try... except block to handle these errors.

After running into an error for the first time, we should observe what type of error we are experiencing (e.g. KeyError, IndexError, ZeroDivisionError, etc.).

Once we know what type of error we need to handle, we should wrap the problematic code inside the try clause, and specify the known error type in the except clause:

print("TOP")

try:
    empty_list = []
    matching_item = empty_list[0] # triggers IndexError (list index out of range)
    print("EVERYTHING IS GOING FINE") # NEVER REACHED
except IndexError:
    print("OOPS - AN ERROR")

print("BOTTOM")
TOP
OOPS - AN ERROR
BOTTOM
print("TOP")

try:
    100 / 0 # triggers a ZeroDivisionError
    print("EVERYTHING IS GOING FINE") # NEVER REACHED
except ZeroDivisionError:
    print("OOPS - AN ERROR")

print("BOTTOM")
TOP
OOPS - AN ERROR
BOTTOM

If we’re not yet sure what type of error we’re experiencing, we can temporarily catch all error classes that inherit from the base error class (Exception), and once caught, we print the specific error’s datatype to learn how to handle it:

try:
    do_something() # some hypothetical problematic code
except Exception as err:
    print(type(err)) #> this will tell you the error type
    print(err) #> the error message

16.2 Raising Errors

If we find the need to trigger our own errors to stop program execution (less common), we can use the raise keyword followed by the type of error (e.g. ValueError):

options = ["rock", "paper", "scissors"]

choice = "hoya" # input("Please choose either 'rock', 'paper', or 'scissors': ")

if choice not in options:
    raise ValueError("OOPS - Please type 'rock', or 'paper', or 'scissors'.")

16.2.1 Defining and Raising Custom Errors

We can define our own errors if that’s helpful, by inheriting a class from the base Exception class (or preferably a more specific one):

class MyCustomError(Exception):
   pass

raise MyCustomError("My custom message")

16.3 Common Errors and Explanations

16.3.1 Syntax Error

A SyntaxError occurs when the code is not written correctly according to the rules of the Python language. This can happen due to missing colons, parentheses, or incorrect indentation.

Example:

# Missing colon
if True
    print("This will cause a SyntaxError")

16.3.2 Value Error

A ValueError occurs when a function receives an argument of the correct type but an inappropriate value. This can happen when trying to convert a string to an integer, but the string does not represent a number.

Example:

# Trying to convert a non-numeric string to an integer
int("hello")  # This will cause a ValueError

16.3.3 Key Error

A KeyError occurs when trying to access a dictionary with a key that does not exist. This can happen when trying to retrieve a value from a dictionary using a key that is not present.

Example:

# Accessing a non-existent key in a dictionary
my_dict = {"name": "Alice"}
print(my_dict["age"])  # This will cause a KeyError

16.3.4 Index Error

An IndexError occurs when trying to access an index that is out of range for a list or other sequence. This can happen when trying to access an element at an index that does not exist.

Example:

# Accessing an index that is out of range
my_list = [1, 2, 3]
print(my_list[5])  # This will cause an IndexError