Debugging with IDLE: Find and Fix Bugs In Your Python Code

Source Node: 1058233

This article was published as a part of the Data Science Blogathon

Introduction

Everyone makes mistakes – even seasoned professional developers, and Python and great at catching them. Let’s see how it works.

Identifying errors is called debugging, and a debugger is a tool that helps to understand the cause of their occurrence. The ability to find and correct errors in the code is an important skill in the work of a programmer, do not neglect it.

IDLE (Integrated Development and Learning Environment) is a cross-platform integrated development and learning environment for Python, created by Guido van Rossum.

Use the debug control window

The main debugging interface in IDLE is the Debug Control window. You can open it by selecting the Debug → Debugger item in the interactive window menu.

Note: If debug is not on the menu bar, make sure the interactive window has focus.

Debug Control Window Overview

To see how the debugger works, let’s write a simple program without errors.

for i in range(1, 4): j = i * 2 print(f"i is {i} and j is {j}")

Save everything, open a debug window, and press F5 – execution has not finished.

The debug window will look like this:

debug control window |Debugging with IDLE

SOURCE

Note that the bar at the top of the window contains the message:

> '__main__'.(), line 1: for i in range(1, 4):

Let’s decipher the code for i in range(1, 4): has not been launched yet, but ‘__main__’.module()reports that at the moment we are in the main section of the program, and not in the definition of the function.

Below the stack pane is the Locals pane, which lists weird things: __annotations__, __builtins__, __doc__, etc. are internal system variables that can be ignored for now. As the program runs, the variables declared in the code and displayed in this window will help you keep track of their values.

In the upper left corner of the window, there are five buttons: Go, Step, Over, Out and Quit – they control the movement of the debugger through the code.

In the following sections, you will learn what each of these buttons does.

Step button

Press Step and the debug window will look like this:

step button | Debugging with IDLE

SOURCE

Note two differences. First, the message in the stack pane has changed:

> '_main_'.(), line 2: j = i * 2:

At this point, line 1 is executed and the debugger stops before executing line 2.

Second, a new variable i with a value of 1 in the Locals panel. The for loop on line 1 created a variable and assigned that value to it.

Continue pressing the Step button to step through the code line by line and watch what happens in the debugger window. When you get to the line print(f” i is {i} and j is {j}”), you can see the output displayed in the interactive window one chunk at a time.

The important thing here is that you can keep track of the growing values ​​of i and j as you go through the for a loop. This is a useful feature of finding the source of errors in the code. Knowing the meaning of each variable in each line of code can help pinpoint the problem area.

Breakpoints and the Go button

Often you know that an error should appear in a certain piece of code, but you don’t know where exactly. Breakpoints tell the debugger when to pause code execution so you can take a look at the current state of the program.

To set a breakpoint, right-click (Ctrl for Mac) the line of code you want to pause and choose Set Breakpoint – IDLE will highlight the line in yellow. To remove it, select Clear Breakpoint.

Set a breakpoint on the line with the print () statement. Now, see the editor window:

debug example | Debugging with IDLE

SOURCE

Save and run. As before, the stacked bar indicates that the debugger is running and waiting for line 1. To move ahead just press the Go button:

save and run

SOURCE

Now on the stack panel, there is information about the execution of line 3:

> '_main_'.(), line 3: print(f"i is {i} and j is {j}")

In the Locals panel, we will see that the variables i and j have values ​​1 and a couple of, respectively. Click the Go button and ask the debugger to run the code until the breakpoint or the top of the program. Press return – the debug window now seems like this:

debug control | Debugging with IDLE

SOURCE

The stack pane displays the same message as before – the debugger is waiting for line 3. However, the values ​​of i and j are now 2 and 4. Press the button a third time. Now i and j are 3 and 6. Pressing Go again exits the program.

Over and Out

The Over button works like a combination of Step and Go — it steps over a function or loop. In other words, if you are going to get into a function using the debugger, you do not need to run the code of this function – the Over button will lead directly to the result of its execution.

Likewise, if you’re already inside a function or loop, the Out button executes the remainder of the code inside the body of the function or loop then stops.

In the next section, we will explore some of the errors and learn how to fix them using IDLE.

Fighting bugs

Let’s take the example of an easy and buggy program.

The add_underscores () method defines below as accepting a String as an argument and returning a new string consisted of underscores before and after each character. For example, it add_underscores(“python”)will return «_p_y_t_h_o_n_».

Here’s the broken code:

def add_underscores(word): new_word = "_" for i in range(len(word)): new_word = word[i] + "_" return new_word
phrase = "hello"
print(add_underscores(phrase))

Enter this code into the editor, save and press F5. The expected output is _h_e_l_l_o_, but o_ is printed instead.

If you find what the problem is, do not fix it. Our goal is to learn how to use IDLE for this.

Let’s consider 4 stages of bug search:

  • guess where the error might be;

  • set a breakpoint and check the code line at a time;

  • define the line and make changes;

  • repeat steps 1-3 until the code works.

Step 1: Guess

You won’t be able to pinpoint the exact location of the error at first, but it’s usually easier to logically imagine which section of the code to look at.

Note that the program is divided into two sections: the definition of the add_underscores () function and the main block, which defines a variable with the value “hello” and outputs the result.

Now, let’s see the code for the main section:

phrase = "hello"
print(add_underscores(phrase))
Obviously, everything is fine here and the problem should be in the definition of the function:
def add_underscores(word): new_word = "_" for i in range(len(word)): new_word = word[i] + "_" return new_word

The first line creates the new_word variable with the value “_”. Miss, the problem is somewhere in the body of the for a loop.

Step 2: breakpoint

Once you’ve identified where the error might be, set a breakpoint at the beginning of the for loop to keep track of what’s going on inside the code:

breakpoint | Debugging with IDLE

SOURCE

Let’s start. Execution stops at the line with the function definition.

Click the Go button to execute the code up to the breakpoint:

go button

SOURCE

The code stops before the for loop in the add_underscores () function. Note that the Locals panel displays two local variables – a word with the value “hello”, and new_word with the value “_”

Click the Step button to enter the for a loop. The debug window changes and therefore the new i variable with a worth of 0 is displayed within the Locals panel:

The variable i is a counter for the for loop that you can use to keep track of the currently active iteration.

debug code

SOURCE

Press the Step button again and look at the Locals panel – the new_word variable is set to “h_”:

find breakpoint | Debugging with IDLE

SOURCE

This is wrong and the reason for this is at first new_word the value is “_”, on the second iteration the value is “_h_”. If we press Step a few more times, we will see that new_word contains the value e_, then l_, and so on.

Step 3: Identify the error and fix it

As we have already found out, at each iteration of the loop, new_word is overwritten with the next character in the line “hello” and an underscore. Since there is only one line of code inside the loop, the problem should be right there:

new_wor = word[i] + "_"

The code tells Python to get the next word character, attach an underscore, and assign the newline to the new_word variable. This is exactly the wrong behaviour that we have observed.

To fix everything, you need to combine the word[i] + “_” with the existing value new_word. Click the Quit button in the debug window, but do not close it. Open an editor window and change the line inside the for loop to the following:

new_word = new_word + word[i] + "_"

Debugger only toggled when idle!

Always press the Go or Quit button when you finish debugging, otherwise, you may have problems restarting it.

Step 4: Repeat steps 1-3 until the error goes away

Save changes in the program and run it again. In the debug window, click the Go button to execute the code up to the breakpoint. Press Step several times and see what happens to the new_word variable at each iteration – everything works needless to say. Sometimes it is necessary to repeat this process several times before the error is fixed.

Alternative ways to find errors

Using the debugger can be tricky and time-consuming, but it is the most reliable way to find bugs in your code. However, debuggers are not always available. In situations like this, you’ll use print debugging to seek out bugs in your code. PD uses the print () function to display text to the console indicating where the program is executing and the state of the variables.

Just as an example, the below code can be added at the end of the loop, for debugging:

print(f"i = {i}; new_wor = {new_wor}")

The modified code will look like this:

def add_underscores(word): new_wor = "_" for i in range(len(word)): new_wor = word[i] + "_" print(f"i = {i}; new_word = {new_wor}") return new_wor
phrase = "hello"
print(add_underscores(phrase))

The output should look like this:

i = 0; new_wor = h_
i = 1; new_wor = e_
i = 2; new_wor = l_
i = 3; new_wor = l_
i = 4; new_wor = o_
o_

PD works but has several disadvantages compared to debugging with a debugger. You should run the entire program every time you want to check the values ​​of variables, and remember to remove calls to print () functions.

One way to improve our loop is to iterate over characters in a word:

def add_underscores(word): new_wor = "_" for letter in word: new_wor = new_wor + letter + "_" return new_wor

Conclusion

Now you know all about DLE debugging. You can use this principle with various debuggers.

In this text, we covered the subsequent topics:

  • using the debug control window;

  • setting a breakpoint for a deep understanding of how the code works;

  • use of the Step, Go, Over, and Out buttons;

Don’t stop learning and practice debugging – it’s fun! and reach me on LinkedIn at https://www.linkedin.com/in/shivani-sharma-aba6141b6/

The media shown in this article are not owned by Analytics Vidhya and are used at the Author’s discretion.

Source: https://www.analyticsvidhya.com/blog/2021/08/debugging-with-idle-find-and-fix-bugs-in-your-python-code/

Time Stamp:

More from Analytics Vidhya