Q3

In this question, you'll be doing some error handling. There's only one function to finish coding below--the logger function--but there's a LOT of other code that's already written for you! This will start introducing you to working with existing codebases.

Part A

In this question, you'll write a logger. This is a critical component of any full-scale application: when something goes wrong, and you start the debugging process, it helps immensely to have logs of activity of your application to see where it started going off the rails.

The log will take the form of a dictionary. The keys of the dictionary will be the name of the method called, and the value will be the integer code of the method's output. OR, in the case of an exception, the integer code of the exception. You can access this code in the except block:

try:
    some_code
except Exception as e:
    e.value

You'll need to save this e.value in your log dictionary for the output of the function call.

Call the unreliable function three times, and store the output of each call in the dictionary with the corresponding key:

  • unreliable1
  • unreliable2
  • unreliable3

Remember: the function output can take the form of its return value (if NO exception occurs), or the integer value of the exception (if an exception DOES occur).

In summary, these are your goals:

  1. In logger(), call the unreliable() function 3 separate times.
  2. When calling unreliable(), wrap it in try / except blocks so no exceptions escape to crash the program
  3. If an exception occurs, record the value of the exception to the dictionary for that function call
  4. If NO exception occurs, record the return value of the function in the dictionary for that function call

In [ ]:
# Finish the function "logger" below.
#
# Note that a dictionary "logs" has been created with 3 keys, all with values of 0.
# To finish the code, you'll need to call the function "unreliable" three times--
# one for each key in the dictionary.
#
# For the most part, "unreliable()" works correctly; if it does so, you need to put
# its return value in the dictionary.
#
# However, sometimes it crashes. If it does, you'll need to
#  1: Catch the exception gracefully (i.e. not crash Python), and
#  2: Store the integer value of the exception in the dictionary
#
# Once you've called "unreliable()" three times and correctly stored the resulting
# outputs in the logs dictionary, just return it and you're finished!

def logger():
    logs = {
        'unreliable1': -1,
        'unreliable2': -1,
        'unreliable3': -1,
    }
    
    ### BEGIN SOLUTION
    
    ### END SOLUTION
    
    return logs

##################################
# DON'T EDIT ANYTHING BELOW HERE #
##################################

# This is the error you'll have to protect against.
class MyError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

# The following function does SUPER IMPORTANT THINGS!
# However, it is very prone to throwing exceptions, so when
# you call it, you'll need to properly protect the calls!
import numpy as np
def unreliable():
    roll1 = np.random.randint(0, 2)
    roll2 = np.random.randint(0, 2)
    if roll1 == roll2:
        raise MyError(roll1)
    return np.random.randint(10, 1000000)

In [ ]:
import numpy as np
np.random.seed(10)
logs = logger()
assert logs['unreliable1'] == 1
assert logs['unreliable2'] == 443722
assert logs['unreliable3'] == 1

In [ ]:
np.random.seed(381734)
logs = logger()
assert logs['unreliable1'] == 961845
assert logs['unreliable2'] == 4913
assert logs['unreliable3'] == 310226

In [ ]:
np.random.seed(75878432)
logs = logger()
assert logs['unreliable1'] == 174254
assert logs['unreliable2'] == 1
assert logs['unreliable3'] == 1