18  Debugging

Debugging is the process of identifying and fixing errors in code. It is an essential skill for programmers to develop. In this course, you will encounter various errors while writing Python code. Understanding how to debug your code effectively will help you become a better programmer.

18.1 Common Types of Errors

Errors in Python code can be broadly classified into three categories:

  • Syntax Errors: These errors occur when the code violates the rules of the Python language. Syntax errors are usually detected by the Python interpreter and are highlighted with an error message.
  • Runtime Errors: Runtime errors occur while the program is running. They can be caused by a variety of factors, such as invalid input, incorrect logic, or unexpected conditions.
  • Logic Errors: Logic errors are the most challenging to identify. They occur when the code runs without throwing any errors but produces incorrect results. Logic errors require careful analysis of the code to identify and fix.

18.2 Debugging Techniques

Here are some common techniques you can use to debug your Python code:

  1. Print Statements: Use the print function to output information to the console.

  2. Breakpoints: Set breakpoints in your code to pause execution at specific lines and inspect the state of variables.

  3. Watch Expressions: Use watch expressions in your IDE to monitor the value of variables as the code executes.

  4. Error Handling: Implement error handling routines to gracefully handle exceptions and provide meaningful error messages.

  5. Testing: Write test cases to verify the correctness of your code and catch errors early in the development process.

  6. Rubber Duck Debugging: Explain your code line by line to an inanimate object (like a rubber duck) to identify issues. Many times, just by articulating the problem, the solution becomes apparent. For example, you can use ChatGPT as a “virtual rubber duck” to help you debug your code.

  7. Logging: Write output to a log file to track the program’s execution and identify errors.

  8. Code Review: Have a peer review your code to identify potential issues and suggest improvements.

  9. Assertions: Use assert statements to validate assumptions in your code and catch unexpected conditions. For example, you can assert that a variable should never be None at a certain point in the code. If it is, the assertion will fail, indicating a problem. Or, you can assert that a value should be within a certain range. For example:

    assert 0 < x < 100, "x should be between 0 and 100"

    If the condition is not met, the assertion will fail, and an error message will be displayed.

18.3 Debugging with VS Code

Visual Studio Code (VS Code) is a popular code editor that provides powerful debugging features for Python. Here are some common debugging features in VS Code:

  • Breakpoints: Set breakpoints in your code by clicking on the line number in the editor. When the code reaches a breakpoint, it pauses execution so you can inspect the state of variables and step through the code. The breakpoints are indicated by a red dot in the editor and can be toggled on and off (enabled/disabled) by clicking on them.
  • Step Over: Execute the current line and move to the next line in the code
  • Step Into: If the current line calls a function, step into the function to debug it
  • Step Out: Finish debugging the current function and return to the calling function
  • Continue: Resume execution until the next breakpoint is encountered
  • Watch: Monitor the value of variables as the code executes by adding them to the watch list in the debugger panel. You can see the value of variables change in real-time as the code runs.
  • Call Stack: View the call stack to see the sequence of function calls that led to the current point in the code execution flow.
  • Debug Console: Use the debug console to execute Python code and interact with the program during debugging.
  • Variables: View the values of variables in the current scope and inspect their contents. The variables are further categorized into Locals and Globals so you can see what belongs to each scope. You can also modify variable values during debugging to test different scenarios.t different scenarios.

Figure 18.1 shows the debugging interface in VS Code with various features highlighted. You can use these features to step through your code, inspect variables, and identify issues. For Python debugging, you could follow the steps outlined on the tutorial: Python debugging in VS Code.

Figure 18.1: In this video, you’ll learn how to get started with debugging in Visual Studio Code (VS Code). The video covers setting breakpoints, stepping through code, inspecting variables, and using the debug console. You can find more information in the official VS Code documentation.

18.4 Try-Except Statement

The try-except statement is used to enable error handling within Python code. There are several ways to use the try-except statement:

  • try-except: This statement tells Python to execute the code in the try block and handle any exceptions that occur in the except block.
  • try-except-else: This statement adds an else block that is executed if no exceptions occur in the try block.
  • try-except-finally: This statement adds a finally block that is executed regardless of whether an exception occurs or not.

In Listing 18.1, we have a function arr_to_string that converts a list of integers to a string. The function handles the case when the list is empty by using an error handler to return an empty string.

Listing 18.1: Example of using the arr_to_string function to convert a list of integers to a string. The function handles the case when the list is empty by returning an empty string. Notice that the try block is used to handle the case when the list is empty, and the except IndexError block is used to return an empty string if an IndexError occurs.
def arr_to_string(arr):
    try:
        result = str(arr[0])
        for i in arr[1:]:
            result += ", " + str(i)
        return result
    except IndexError:
        return ""

def test_arr_to_string():
    arr = [10, 20, 30, 40, 50]
    assert arr_to_string(arr) == "10, 20, 30, 40, 50"
    
    arr = [10]
    assert arr_to_string(arr) == "10"
    
    arr = []
    assert arr_to_string(arr) == ""

test_arr_to_string()