Some material from Jake Vanderplas' Error handling tutorial.
Instructions: Create a new notebook called ErrorHandlingExercises in your ErrorHandling directory and solve the problems inside it. Be sure to include the problem statement in a markdown cell above your solution. You don't need to put the "helper" code in the markdown cell, just implement the helper code in your code cell with your solution.
Preliminaries: At the top of your notebook, include a "Heading 1" cell with the title Error Handling Exercises.  Then include the inline functions and libraries you need by adding a code cell that invokes the %pylab inline magic and imports the needed packages.
Find a partner.  Choose a driver and a navigator.  Copy your Battleship code to a cell in this ErrorHandlingExercises notebook.  Work together to add some error handling to the program.  Include a markdown cell identifying the driver and navigator.  When you are done, switch roles and let the navigator be the driver and vice versa.  At the end you should both have a Battleship program that includes try...except error handling for the following situation:
Here is a function which takes a filename, opens the file, reads the result, closes the file, and returns the contents. It should look something like this:
In [ ]:
    
def read_all_from_file(filename):
    f = open(filename)
    contents = f.read()
    f.close()
    return contents
    
Use runtime error handling concepts to improve this function, so that it does the following:
try, except, else, and/or finally to safely return the file's contents & close the file if necessary.safe which defaults to False. If the function is called with safe=True, then return an empty string if the file doesn't exist.
In [ ]:
    
def read_all_from_file(filename):
    # your code here
    
In [ ]:
    
read_all_from_file('tmp.txt')
    
In [ ]:
    
read_all_from_file('file.which.does.not.exist')
    
In [ ]:
    
read_all_from_file('file.which.does.not.exist', safe=True)