Debugging Python code

As already stated in the introduction to this chapter, knowing the debugger of the language you are developing in and how to put it to use is part of the bread-and-butter business of a programmer, just like writing the code in the first place. In case of Python, this means knowing how to use the pdb debugger, which will be introduced throughout the course of this recipe.

The pdb debugger operates as an interactive prompt, accepting simple text commands that trigger actions like advancing program execution, or setting break points that stop program execution at a given line. In the following tasks, you will be walked through an example debug session, teaching you all the commands needed for analyzing code and hunting down those nasty bugs!

However, note that pdb is not the only debugger available for Python. There are plenty of alternatives available like pydbgr, pudb, or Winpdb that might provide more features or a user interface that's easier to use. Though the big plus point for pdb, on the other hand, is that it comes included with the Python runtime, that's part of the Panda3D engine.

Getting ready

The program you are going to debug is the one developed in the recipe Managing recurring tasks, found in Chapter 7, Application Control. Please prepare the code and before proceeding, make the following changes to the source code:

  1. Delete the removeSmileys() method.
  2. Find and delete the following line in the constructor:
    taskMgr.doMethodLater(60, taskMgr.remove, "RemoveUpdate", extraArgs = ["UpdateSmileys"])
    
  3. Remove the uponDeath parameter from the call that adds the updateSmileys() method to the task manager.

Finally, check if Panda3D's bin directory can be found in the system search path. You can do this by opening a command prompt and issuing the command ppython. This should start an interactive Python session.

How to do it...

Let's take a look at the Python debugger:

  1. Open a new command prompt window and navigate to the src subdirectory of your project directory.
  2. Use the following command to start debugging the program:
    > ppython m pdb main.py
    
  3. In the newly opened command prompt, we first want to get an overview of the available commands:
    (Pdb) help
    
  4. Place a temporary breakpoint at the first line of the addSmiley() method:
    (Pdb) tbreak Application.py:15
    
  5. Create another breakpoint at the updateSmileys() method and set a condition for triggering the breakpoint:
    (Pdb) break Application.py:27
    (Pdb) condition 2 self.smileyCount > 50
    
  6. List all active breakpoints:
    (Pdb) break
    
  7. The program is in a halted state. Continue execution by entering the following command:
    (Pdb) continue
    
  8. After a short moment of running, the application is stopped at the first breakpoint. Gather some context about where the execution flow was stopped. List an excerpt of the source code surrounding the breakpoint:
    (Pdb) list
    
  9. Inspect the arguments that were passed to the method:
    (Pdb) args
    
  10. Execute the addSmiley() method until the point from which it is going to return:
    (Pdb) return
    
  11. Print the value of the smileyCount variable:
    (Pdb) p self.smileyCount
    
  12. Continue execution until the next breakpoint is triggered:
    (Pdb) c
    
  13. Display a stack trace:
    (Pdb) where
    
  14. Execute the current and next line of code:
    (Pdb) next
    (Pdb) n
    
  15. Check the type of the local variable vel:
    (Pdb) whatis vel
    
  16. Change the value of the smileyCount variable and continue execution:
    (Pdb) !self.smileyCount = 55
    (Pdb) c
    
  17. Clear the breakpoint in updateSmileys() and let the program continue execution:
(Pdb) clear 2
(Pdb) continue

How it works...

The pdb debugger is actually implemented as a Python module, which we load from the library search path using the -m pdb parameters we pass to the Python runtime. This starts up the debugger's command shell, loads our main.py file, and pauses program execution.

We then go on to add breakpoints to our code. A breakpoint marks a line of code so the debugger halts the program when it is reached while the program is executed. While a standard breakpoint, created with the break command, causes program execution to be stopped whenever it is hit, we add some special cases of breakpoints: A temporary and a conditional breakpoint.

Temporary breakpoints are deleted after being hit once. This is useful if you're only interested in the first iteration of a loop, for example. If you want to narrow down the cause of a bug in more detail, or want to skip loop iterations, conditional breakpoints allow the contents of variables to be examined and evaluated. Only if the expression provided evaluates to true, the program gets stopped.

Beneath breakpoints, another set of commands is dedicated to controlling how to run the program. Using next, return, and continue, we are able to execute step by step, to the point where the current function is about to return, or until the next breakpoint is hit.

Additionally to these commands, there's step, which we didn't use in this recipe. This command steps the program line by line just like next, with the exception that instead of stepping over function calls, it jumps into the body of the function being executed. This makes it possible to observe what's going on inside a function instead of just seeing the result or return value of the current call.

The last group of commonly used commands is used for gathering information about where in the code we currently are (list), how we got there (where), and the types as well as the values of variables and function parameters (whatis, p, args). These are the essentials for observing program state and finding problems with our code!

One additional great thing about this debugger is the possibility to execute Python code directly within the debug session. Lines beginning with an exclamation mark are directly passed to the Python runtime to be interpreted, which makes it possible to halt execution using a breakpoint, change the value of a variable or the state of an object before proceeding the execution. This can be quite useful for quickly trying fixes or finding program states that cause erroneous behavior.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset