Learn to Code via Tutorials on Repl.it

← Back to all posts
13
Debugging in python, fun and easy!
h
21natzil (680)

Debug Without Errors

Debugging is the most dreaded part of programming. It should just work! And sometimes bugs can lead to hours of extra work. However they don't need to be, and by following some of the steps presented in this guide, hopefully you'll be able to fight your next bugs with vigor and valiance!

In this tutorial, we'll be using this program, which is intentionally broken. Follow along and make the changes we talk about, and see it be fixed right before your eyes! (Don't try to figure out what it does, it just does random things).

Alright, so when we run this program we get...

The max number cannot be 0
The max number cannot be 0
The max number cannot be 0
The max number cannot be 0

Oh no! We didn't even get an exception! Where do we even begin? Forchanetly, this program is very small. However for larger projects this is a huge issues. A smart place to start, would be the line that displays the output. In this case, it's

except:
    print("The max number cannot be 0")

Now, in this program we only want this error to be raised in the max_number is 0! Clearly, the condition that must be met to trigger this line is wrong. The condition in this scenario is a try-except case, with no defined exceptions! This is bad code 101, also define what exception you want to catch, else wise python will catch all of them. Plus, the message doesn't need to be in a try-except statement. We can create an if-statement at the start of the function, where is the max number is 0, it will print the message and exit the function. If we remove the try-except statement and add the if statement, the function will look like this:

def handle_math(min_number: int, max_number: int):
    if max_number == 0:
        print("The max number cannot be 0")
        return

    print(
      get_data() * int(min_number / max_number)
    )
    return  (min_number / max_number)

Great problem solved and if we run that...

The max number cannot be 0
The max number cannot be 0
The max number cannot be 0
The max number cannot be 0

What? We just fixed this error! Why is it still here? Don't panic, we know it's not the try-catch anymore. Think about it reasonably, this means that the code calling the function is calling it wrong, so let's take a look at it.

numbers = [0,  1,  2,  3]

for i in range(len(numbers)):
    r = handle_math(5, numbers[i])
    numbers.insert(0, r)

This is a great time to use the rubber duck technique. The idea is you read out your code to a rubber duck, although the rubber duck is optional. So let's do just that, we make a list, create a range from 0-3, call handle_math with 5 and the item in the list at an index from 0-3. Then we update the items in the list by adding the result of handle_math to the start of the list. Did you catch the error? Maybe we should visualize what's happening. The list when we start looks like the this withi being 0.

Items:0123
Index:0123

The handle_math we return None, because the value passed in was 0. Then None is added to the start of the list, and then the loop increments i so it's now 1, however now the list looks like:

Items:N0123
Index:01234

As you can see, the 0 moved up, and so it's index is 1. The range generator doesn't compensate for that, and so i will always be 0. To fix this, instead of iterating the index, let's iterate through each element in the list, which will look like:

numbers = [0,  1,  2,  3]

for i in numbers.copy():
    r = handle_math(5, i)
    numbers.insert(0, r)

Phew, we're finally done! Now if we run this we'll get:

The max number cannot be 0
Traceback (most recent call last):
  File "main.py", line 22, in <module>
    r = handle_math(5, i)
  File "main.py", line 15, in handle_math
    get_data() * (min_number / max_number)
  File "main.py", line 7, in get_data
    return resp.content.decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0x81 in position 4: ordinal not in range(128)

Oh no, another bug! When will it end. Don't fret, we're actually making progress, and we can locate the current error with more ease! The problem now is this error message is super complicated. 'ascii' codec can't decode byte 0x81 in position 4: ordinal not in range(128)? What does that even mean? Errors like these are can be intimidating, but through the power of google, we will prevail! If you simply paste your exception into google, the first site is a stackover flow post which has a response that goes into great detail. If we read that, we'll realize that the issue is we're decoding bytes into ascii, which can't represent all the values in our data. Instead, we should use uft-8, which can handle all these values. Now that function looks like:

def get_data(size=16):
    with requests.Session() as session:
        with session.get(f"https://httpbin.org/stream-bytes/{size}")  as resp:
    return resp.content.decode('utf-8')

Wew, nice! Now that we've done our research, we should be in the clear right? Running our code now gives us:

The max number cannot be 0
<<<<<
<<
<

Which is actually what we wanted! We're done! I know, this looks random and trust me it is, I just needed a broken program. I hope you learned much about debugging, and maybe I'll make a follow up using pdb if this is well received. You can find the finished, debugged version here.

Have a great day!

Commentshotnewtop
1
themaka (176)

Very nice @21natzil ! Debugging is a very important skill and can often be more difficult that programming in the first place.

Let @21natzil know if there's any feedback for this!

1
HUDSON_REYREY (0)

This is pretty good! I love it! <3 I find it easy.