Lesson 35:

The Raise and Assert Statements

Invalid code raises exceptions:


In [1]:
42/0


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-1-d8fe8f9e37b9> in <module>()
----> 1 42/0

ZeroDivisionError: division by zero

Formerly, the try and except statements were used to handle exceptions, but you can also create your own exceptions with raise.


In [2]:
raise Exception('This is the error message.')


---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-2-06c4ed19fb7b> in <module>()
----> 1 raise Exception('This is the error message.')

Exception: This is the error message.

Box Priting Program


In [12]:
def boxPrint(symbol, width, height):
"""

A program to build boxes like this:

***************
*             *
*             *
***************

"""
    # print top part
    print(symbol * width) 
    # print the sides (excluding top and bottom)
    for i in range(height-2):
        # empty spaces in the middle except the last two spots
        print(symbol + (' ' * (width - 2) + symbol))
    # print the bottom
    print(symbol * width) 
    
# Working functions
boxPrint('*', 15,5)
boxPrint('O', 10,8)

# Not working
boxPrint('**', 5,16)


***************
*             *
*             *
*             *
***************
OOOOOOOOOO
O        O
O        O
O        O
O        O
O        O
O        O
OOOOOOOOOO
**********
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**   **
**********
None

To handle situations with the wrong input, an exception can be raised:


In [23]:
def boxPrint(symbol, width, height):
    """

    A program to build boxes like this:

    ***************
    *             *
    *             *
    ***************

    """
    # Exception statements
    if len(symbol) != 1:
        raise Exception('"symbol" needs to be a string of length 1.')
    if (width < 2) or (height <2):
        raise Exception('"width" and "height" must be greater than or equal to 2.')
    
    print(symbol * width) 
    for i in range(height-2):
        print(symbol + (' ' * (width - 2) + symbol))
    print(symbol * width) 
    
# Working functions
boxPrint('*', 10,5)
boxPrint('O', 12,8)


**********
*        *
*        *
*        *
**********
OOOOOOOOOOOO
O          O
O          O
O          O
O          O
O          O
O          O
OOOOOOOOOOOO

In [20]:
# Exception statement 1
boxPrint('**', 5,16)


---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-20-96da8a8d17c3> in <module>()
      1 # Exception statement 1
----> 2 boxPrint('**', 5,16)

<ipython-input-18-e1bfffaa3da3> in boxPrint(symbol, width, height)
     12     # Exception statements
     13     if len(symbol) != 1:
---> 14         raise Exception('"symbol" needs to be a string of length 1.')
     15     if (width < 2) or (height <2):
     16         raise Exception('"width" and "height" must be greater than or equal to 2.')

Exception: "symbol" needs to be a string of length 1.

In [19]:
# Exception statement 2
boxPrint('*', 1,1)


---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-19-96e1f37dbdb4> in <module>()
      1 # Exception statement 2
----> 2 boxPrint('*', 1,1)

<ipython-input-18-e1bfffaa3da3> in boxPrint(symbol, width, height)
     14         raise Exception('"symbol" needs to be a string of length 1.')
     15     if (width < 2) or (height <2):
---> 16         raise Exception('"width" and "height" must be greater than or equal to 2.')
     17 
     18     print(symbol * width)

Exception: "width" and "height" must be greater than or equal to 2.

Exceptions have a particular structure, defined as the traceback or call stack.

The traceback shows exactly what function the error occured in, here boxPrint(**,5,16) and boxPrint(*,1,1). It also shows where the exception was define in the program, lines 14 and 16.

The traceback module contains some tools to deal with the traceback, including returning it as a string value.

This is useful for storing code in a log so it doesnt break the entire function on error.


In [28]:
import traceback
import os

# Sample code to raise exception and store code in a log
try: 
    # Raise this exception
    raise Exception('This is the error message.\n')
except: 
    # Open/create a log in append mode
    errorFile = open(os.path.abspath('files/error_log.txt'), 'a')
    errorFile.write(traceback.format_exc())
    errorFile.close()
    print('The traceback info was written to error_log.txt')


The traceback info was written to error_log.txt

In [29]:
# Open the error file and read the log; will increase for every error
errorFile = open(os.path.abspath('files/error_log.txt'), 'r')

errorFile.read()


Out[29]:
'Traceback (most recent call last):\n  File "<ipython-input-25-7eda8e597156>", line 6, in <module>\n    raise Exception(\'This is the error message.\')\nException: This is the error message.\nTraceback (most recent call last):\n  File "<ipython-input-28-eaa4343c9c13>", line 7, in <module>\n    raise Exception(\'This is the error message.\')\nException: This is the error message.\n'

An assertion is a sanity check to make sure the code isn't doing something obviously wrong. It is another kind of exception.

If an assert statement evaluates to false, then an error is raised.


In [30]:
assert False, 'This is the error message.'


---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-30-4bfd86b0e34a> in <module>()
----> 1 assert False, 'This is the error message.'

AssertionError: This is the error message.

Stoplight Program


In [33]:
market_2nd = {'ns':'green', 'ew':'red'} #ns: north south, ew: east west

def switchLights(intersection):
    for key in intersection.keys():
        if intersection[key] == 'green':
            intersection[key] = 'yellow'
        elif intersection[key] == 'yellow':
            intersection[key] = 'red'
        elif intersection[key] == 'red':
            intersection[key] = 'green'
        

print(market_2nd)
switchLights(market_2nd) # Run function in data structure
print(market_2nd)


{'ns': 'green', 'ew': 'red'}
{'ns': 'yellow', 'ew': 'green'}

This code is buggy, because the 'north-south' direction is green, while the 'east-west' direction is yellow. The cars crash into each other at intersections.

We need to create a sanity check with an assert statement to make sure illogical things throw errors.


In [34]:
market_2nd = {'ns':'green', 'ew':'red'} #ns: north south, ew: east west

def switchLights(intersection):
    for key in intersection.keys():
        if intersection[key] == 'green':
            intersection[key] = 'yellow'
        elif intersection[key] == 'yellow':
            intersection[key] = 'red'
        elif intersection[key] == 'red':
            intersection[key] = 'green'
        # if false, raise assert statement
        assert 'red' in intersection.values(), 'Neither light is red at ' + str(intersection) + '!'

print(market_2nd)
switchLights(market_2nd) # Run function in data structure
print(market_2nd)


{'ns': 'green', 'ew': 'red'}
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-34-6ea4376e7220> in <module>()
     13 
     14 print(market_2nd)
---> 15 switchLights(market_2nd) # Run function in data structure
     16 print(market_2nd)

<ipython-input-34-6ea4376e7220> in switchLights(intersection)
     10             intersection[key] = 'green'
     11         # if false, raise assert statement
---> 12         assert 'red' in intersection.values(), 'Neither light is red at ' + str(intersection) + '!'
     13 
     14 print(market_2nd)

AssertionError: Neither light is red at {'ns': 'yellow', 'ew': 'green'}!

An assert statement asserts that a condition holds True. If the assert is False, then it raises an assert statement.

This improves the ability to debug, by holding the program to logical output.

Assertions should be used for programmer errors (invalid outputs, wrong returns, etc.) They are meant to be recovered from.

Exceptions should be used for user errogs (invalid inputs, bugs, etc.) They are meant to stop invalid program use.

Any error statements will help improve the debug flow, and help you find errors sooner instead of later.

Recap

  • You can raise your own exceptions with raise Exception('Error message').
  • You can also use assertions with assert condition,'Error message'.
  • All Error messages return a traceback, outlining the line the error occured, and the actual error logic.
  • Assertions are for detecing programmer errors that are not meant to be recovered from.
  • User errors should raise exceptions.