08-Debugging#

  • Errors

    • Syntax

    • Exceptions

  • Debugging Process

    • Stack trace

    • Using outside resources

  • try/except

Debugging is the process of finding and fixing errors in a computer program.

Logistics#

  • Wed Jan 30:

    • CL4

  • Fri Feb 2:

    • ~~Exam 1 in class component~~ ONE WEEK LATER!

    • A2

  • Mon Feb 5

    • ~~Exam 1 take home component due~~ ONE WEEK LATER!

  • Exam info

    • in class component

      • will largely be multiple choice and short answer

      • will largely test your understanding of code, not ask you to write it

      • will test your knowledge of concepts and jargon and code style

      • no notes or cheat sheet allowed

      • paper or Canvas (uncertain which right now)

    • take home component

      • will look like an assignment, on Datahub

      • will ask you to create code to solve problems

      • open notes

      • do not seek help from an AI or a person

    • Practice exam will be availble or we will use Wed lecture / coding labs to review (uncertain which right now)

Errors#

Errors are problems with code definition or execution that interrupt running Python code.

Syntax Errors#

  • Syntax Errors

  • Indentation Errors

Syntax & Indentation Errors reflect code that doesn't follow Python structure, and will necessarily fail.

Syntax Error Examples#

# will produce a syntax error
if True
    print('Yep.')

Python does its best to tell you:

  • what type of error it is

  • and where it thinks it occurred (^)

# will produce a syntax error
# and specifically an indentation error
del value
if 3 < 5:
    print(value)

Python gives you a readout about what it was expecting and where you appear to have gone wrong.

Exceptions#

Exceptions are errors that occur when a code is executed.

For these, there’s nothing wrong with the syntax or structure of the code, but in your specific case and how you’re trying to use it, Python says ‘no’.

ZeroDivisionError#

ZeroDivisionError occurs when you try to divide by zero.

# produces ZeroDivisionError
1 / 0

Python specifies:

  • the Exception and specific type / error

  • points you to where the error occurred

NameError#

NameError occurs when you try to access a name that Python does not know.

# Define a variable
variable = 12
# If you typo a name, you will get a NameError
varaible

While it’s annoying, it’s helpful that Python doesn’t just guess that you meant ‘variable’….because sometimes Python would guess wrong. It’s better for Python to just give us the error.

# You also get a name error if you try to use the wrong operator for assignment
new_variable == 1

IndexError#

IndexError occurs when you try to access an index that doesn’t exist.

my_string = 'COGS18'
my_string[6]
# Relatedly, 'KeyError' occurs if you ask for a dictionary key that doesn't exist
my_dictionary = {'name1' : 1, 'name2' : 2}
my_dictionary['name3']

ValueError#

ValueError occurs when you try to use an illegal value for something.

int('cat')

TypeError#

TypeError occurs when you try to do something with a variable type that python cannot interpret.

'a_string' + 12

Error Recap#

  • Syntax Errors

    • SyntaxError

    • IndentationError

  • Exceptions

    • ZeroDivisionError

    • NameError

    • Index Errors

      • IndexError

      • KeyError

    • ValueError

    • TypeError

Class Question #1#

What type of error will the following code produce?

if num > 0
    print("Greater than 0")
  • A) Syntax

  • B) Name

  • C) Type

  • D) Index

  • E) Value

Class Question #2#

What type of error will the following code produce?

if num > 0:
    print("Greater than 0")
  • A) Syntax

  • B) Name

  • C) Type

  • D) Index

  • E) Value

Stack Trace#

Read (and understand!) your error messages!

The trace (log) of what Python did as it went through your code. Gets printed out if Python runs into an error.

For example…say you’re trying to write a function that takes a character as input, turns it into its unicode code point…with an offset of 500, turns that back into a character and returns the output…Your first attempt (which has errors) is below:

def encode_char(char):
    # turn into code point
    ord(char)
    # add offset
    char + 500 = offset
    # turn into character
    chr(offset)

What to look for and think about:

  1. What kind of error is it?

  2. What line of code/where in the line of code is the error pointing to?

  3. What does it mean? How do I fix this?

Sometimes these get really complex. We’re here to get better at interpreting these traces. Note that if external functions are being used, these will get longer.

Sometimes you even have assert messages to guide you…how should you use assert messages?

  1. Read them; understand them

  2. Revisit the part of your code most likely associated with the assert

  3. Think about what you would need to change to fix the issue (do not just guess wildly trying to pass the assert - will waste your time)

  4. Fix the code; re-execute; re-run the assert

assert callable(encode_char)
assert type(encode_char('c')) == str
assert encode_char('c') == 'ɗ'

Helping yourself when debugging#

  • Pause and think

  • You aren’t sure how to approach a problem: add ‘in python’

  • You don’t understand an error message:

    • Use print() statements

    • Google the error message…but then you need to understand what you’re reading

Unsure how to approach a problem?#

“Write code that will check whether the value stored in my_val is both positive and even. If it is, store the integer 1 in the variable output

…How do I check whether a variable stores an even value?

Google: “How to check whether a variable stores an even value in python

ChatGPT: How would I write a function in python to determine whether a variable stores an even value?

Don’t understand the error message?#

So you try to accomplish the task…below is your first attempt

# this code has errors
# we're going to debug together in class
my_val = 6

if my_val > 0 and my_val % 2 = 0:
    output == 1

Try / Except#

Exceptions do not necessarily have to lead to breaking the program - they can be programmatically dealt with, using try and except.

For this example, I’m going to introduce input(), a function which allows us to get input from the user.

# Example: we want to get an input number from the user

my_num = input("Please type a number: ")

print('\nmy_num is: ', my_num)

try / except Block#

try:
    int(input('Number'))
except:
    print("nahhh")

Raising Errors#

You can also write code to raise an Exception if something unexpected happens.

Raise Exception Examples#

raise is a keyword that tells Python you want to create your own error.

my_int = input('An integer please: ')
if not my_int.isnumeric():
    raise ValueError('I wanted a number! :(')
    
print('My integer is: ', my_int) 

Class Question #3#

Edit the code below (replacing --- with either values or variable names) so that when executed, this cell returns None.

num1 = ---
num2 = ---

try:
    output = num1 / num2
except ZeroDivisionError:
    output = None
    
print(output)
  • A) I did it!

  • B) I think I did it…

  • C) I’m totally lost.

Debugging a program#

Figure out the problem by asking yourself the right questions.

Before you start debugging, make sure you’ve identified the problem you’re trying to solve:

  • What did you expect your code to do?

  • What happened instead?

It might even be helpful to write these things down!

In making explicit what you expected your code to do, you have stated some assumptions. The next natural step is to test each and every one of those assumptions.

Typical assumptions you might make are:

  • A particular function you are using (e.g. math.pow) works with two arguments in a certain order (e.g., exponent, number)

  • A particular variable will have value I expect (e.g. a number between 1 and 10)

  • I have made no typos in my code

  • The bug only appeared after I edited function X… therefore the problem must be in the changes I made to function X

The reason we test assumptions is because they MAY NOT BE TRUE. All of the things in the list above are all things I assumed while programming that turned out to be wrong. It is important to remember how Sherlock Holmes solves cases

When you have eliminated the impossible, whatever remains, however improbable, must be the truth

To test your assumptions you must set out to prove them wrong. Do not try to show your assumptions are correct… set out to show they are wrong.

Some advice for testing your assumptions#

  1. Check suspect code line by line to look for syntax error style problems (the wrong ‘ or “, missing brackets, bad indentation, a typo in a variable name).

  2. Look 2 or 3 times at program control points (if statements). Is the condition actually what you intended/assumed? E.g., you might have accidentally assigned a student an A if they scored >89%… the correct would be >=90%.

  3. Use print()! Print out variable values at different points in the program to make sure they return what you expect. Watching a variables value change as you execute the program can show you that something unexpected is occurring at a particular point in the program… leading you to finally see the typo or bad logic at the heart of the problem.

  4. Read the error output in the terminal. It tries to be helpful, but it will often not quite point you towards the problem. It can at least give you a ballpark location in the code and some things to look for.

  5. Get sections of the code working one by one. If you are sure that section A is bug free, you can narrow your search for the problem to section B or C.

  6. Read the Python documentation for the methods you are employing.

  7. Comments throughout your code to make sure you understand what every step is doing. This will help you pinpoint problems.

  8. Ask for help! Working with an expert to debug is going to make your debugging skills take flight.

# Here's an exercise for you to debug.  Apply your new skills!

greeting = input("Hello, possible pirate! What's the password?)
if greeting in ["Arrr!"):
    print("Go away, pirate.")
elif
print("Greetings, hater of pirates!")
# Here's an exercise for you to debug. Apply your new skills!
# hint, maybe you want to print out the contents of out
# after every line of code to see how it changes

def string_spinner(a_string):
    '''
    string_spinner
        input: a_string (any string)
        output: the same string but backwards and with Spanish-style ¡ and ! added front and back
    '''
    out = a_string
    a_string = a_string[::-1] # this reverses the string
    return '¡'+out+'!'


# this should print
# '¡ogge ym oggel!'
# but it doesn't
string_spinner('leggo my eggo')