winpdb—a platform independent python debugger

53
Winpdb—A Platform Independent Python Debugger http:// winpdb.org /

Upload: denis-snow

Post on 05-Jan-2016

226 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Winpdb—A Platform Independent Python Debugger

Winpdb—A Platform Independent Python Debugger

http://winpdb.org/

Page 2: Winpdb—A Platform Independent Python Debugger

Installation for Windows First install the wxPython runtime (unicode version preferred)

http://www.wxpython.org/download.php#binaries

Download (click on) wxPython2.8-win32-unicode-py27 and execute

wxPython is interesting in its own right

It’s a wrapper for the cross-platform GUI toolkit wxWidgets (written in C++) for Python

It’s 1 alternative to Tkinter, which is bundled with Python

There is (on the same page) also an installer containing the wxPython demo, other samples, and wxWidgets documentation:

wxPython2.8-win32-docs-demos (an .exe file)

Page 3: Winpdb—A Platform Independent Python Debugger

After installing the wxPython runtime, go to Winpdb download page

http://winpdb.org/download/

Under the heading “Winpdb for Windows”, click the “Winpdb source package” to get to

http://code.google.com/p/winpdb/downloads/detail?name=winpdb-1.4.8.zip&can=1&q=

Download the zipped Winpdb source package

Extract it’s content into a new folder (say, under C:\Python27)

Enter the new folder from a CMD terminal window and type

python setup.py install

Page 4: Winpdb—A Platform Independent Python Debugger

Getting Started: simple.py See the tutorial at

http://code.google.com/p/winpdb/wiki/DebuggingTutorial

Start with a simple script, simple.py

"""A simple script.""“

a = 1b = 2

c = a + b

print c

In a CMD window, go to the folder with simple.py and type winpdb simple.py

Page 5: Winpdb—A Platform Independent Python Debugger

The Winpdb window opens and begins “attaching” to the script (the “debuggee”)

There’s also a blank output terminal open in a separate window

The debugger launched the script

Halted execution before the 1st line was executed

Awaits your command

Page 6: Winpdb—A Platform Independent Python Debugger

The Winpdb Window The Winpdb window has several parts

Source Browser

Appears in the top right of the window The “current line” (the next line

to be executed) is highlighted

The “C” next to the line number “1” is the status indicator

Page 7: Winpdb—A Platform Independent Python Debugger

Console

Just below the source browser

Lets us interact with the debugger directly

Menu and Controls

At the top of the window

Explore this as you like

Page 8: Winpdb—A Platform Independent Python Debugger

Debuggee State These frames monitor the state of debuggee

Appear in the left side of the window

The Namespace frame keeps track of variables and their values as the debugger executes the code Tracks debuggee data among 3 levels

Locals: data within the current scope Globals: data within the global scope Exception: for examining exception data

The Threads frame gives info on threads of the debuggee (Advanced)

The Stack (“S”) window frame displays the call stack ( “s”) Top frame is most recently called rpdb2.py (debugger backend) always at

lowest levels

Page 9: Winpdb—A Platform Independent Python Debugger

Control Basics Focus on left half of control panel—explore right half buttons on your own

The Go button lets the debuggee run to whichever comes first

a breakpoint,

an exception, or

the end of the program

With no breakpoints or exceptions encountered, the debuggee runs as if executed from the Python interpreter

Page 10: Winpdb—A Platform Independent Python Debugger

Click the Go button

The debugger moves through each line and down through the call stack and finishes running the script

The source browser shows the current line as rpdb2.setbreak()—a lot of code

The Stack frame wnidow shows only 1 frame in the stack, frame 0—relates to rpdb2.py

The terminal window opened by Winpdb contains “3”—output from simple.py

To restart the debugging session, click Restart under the File menu

Page 11: Winpdb—A Platform Independent Python Debugger

The Next button executes the current line and proceeds directly to the next statement in the current scope

The status indicator character is currently at C—the debugger just entered the code at the current scope

Not actually ready to execute the current line

Click Next: The status character change from C to L

L indicates the debugger is prepared to execute the current line

Click Next again: The debugger moves to the next line, a = 1

Click Next again: The debugger executes this statement

Page 12: Winpdb—A Platform Independent Python Debugger

The debugger now tracks the variable a and its value in the Namespace frame, at the bottom of the Locals tab

It shows type int and value 1

Click Next again: Execute b = 2

The Namespace frame now contains b as well as a

Click the Globals tab: both a and b are there too

Click Next again: c now appears in the Namespace frame

Page 13: Winpdb—A Platform Independent Python Debugger

print c is the current line

Click Next: The status character changes from L to R

Indicates the current scope is complete and is prepared to return

Click Next again to allow the return

The debugger has started moving back through the stack

rpdb2.py is the file of the current stack frame

The calling function is StartServer

Page 14: Winpdb—A Platform Independent Python Debugger

The source browser has updated to show the code of the new current line, now in the rpdb2.py file

The status character is R again: this scope is also prepared to return

Click Next until you reach the line with rpdb2.setbreak()

Same line the debugger ended on when we clicked the Go button

Page 15: Winpdb—A Platform Independent Python Debugger

Breakpoint Basics A breakpoint is an instruction that tells the debugger to pause

execution once it reaches that line

Set a breakpoint for the line with the statement c = a + b

c is about to be defined

Restart the session

In the source browser window,

position your mouse between the line number and the statement c = a + b

Left-click

The line turns red, indicating a breakpoint

Page 16: Winpdb—A Platform Independent Python Debugger

Can now run the code straight to this point, without having to click Next repeatedly

Click the Go button

The code executes and halts at the line with the breakpoint

Can inspect the values of a and b

Click Next to execute the statement and see the value of c

Click Go to finish execution of the debuggee

To unset a breakpoint, click on the line the same way we did to set the breakpoint

The highlighting disappears

Can set as many breakpoints as we like

Page 17: Winpdb—A Platform Independent Python Debugger

Console Basics The console has all of the commands available from the control

panel and the menus—and many more

To restart the debugging session, click inside the console’s command area

Type in restart and Enter

Can execute the current line of code by typing in next (or just n)

Enter n to enter in the current scope,

then n again to execute the 1st statement,

and again to execute the 2nd statement

Page 18: Winpdb—A Platform Independent Python Debugger

Now at the statement c = a + b

Console command exec (or x) takes a single statement as an argument

Executes that statement in the context of the current scope.

Currently, a is 1 (see the Namespace frame)

To change a’s value to 3, enter in the Command box

exec a = 3

The current statement is c = a + b

Click Next to execute it

Unlike before, the value of c is now 5

exec lets us create new variables in the current scope—e.g.,

x d = 4

Check the Namespace frame

Page 19: Winpdb—A Platform Independent Python Debugger

Console command eval (or v) takes a single expression as its argument

Returns (in the trace produced in the console) the results of that expression in the debuggee’s current scope

Try the following

eval a + b

v b + d

Unlike exec, eval doesn’t affect the state of the debuggee

Page 20: Winpdb—A Platform Independent Python Debugger

Console command jump (or j) takes a single argument, an integer representing the line number to jump to (forwards or backwards)

E.g., to reset a back to its original value, can jump back to the statement a = 1

Enter at the console

jump 3

The current line jumps back up to line 3

The status character indicates that the debugger is prepared to run the statement

Click the Next button to run it and change a back to 1

Page 21: Winpdb—A Platform Independent Python Debugger

Now reset the value of c, which depends on a, back to what it was

We haven’t affected the value of b

So we can jump over that line to the line with c = a + b

jump 6

Executing this line changes c to 3

Page 22: Winpdb—A Platform Independent Python Debugger

Wrap Up Covered the following:

What each frame of the Winpdb window displays and allows us to control

How to run a whole script, how to run a single line

How to restart a debugging session

How to set a simple breakpoint

How to manipulate variables and evaluate expressions in the current scope

Page 23: Winpdb—A Platform Independent Python Debugger

Diving Deepe: divisibles.py The debuggee is now divisibles.py

A more complex script, with a function and a loop

Should take a positive integer and print out all integers that divide that integer

"""

Takes a positive integer as an argument and prints all of its divisors.

example usage:

python divisbles.py 100

"""

import sys

Continued

Page 24: Winpdb—A Platform Independent Python Debugger

def is_divisible(a, b):

"""Determines if integer a is divisible by integer b."""

remainder = a % b

# if there's no remainder, then a is divisible by b

if not remainder:

return True

else:

return False

Continued

Page 25: Winpdb—A Platform Independent Python Debugger

def find_divisors(integer):

"""Find all divisors of an integer and return them as a list.""“

divisors = []

# we know that an integer divides itself

divisors.append(integer)

# we also know that the biggest divisor other than the integer itself

# must be at most half the value of the integer (think about it)

divisor = integer / 2

while divisor >= 0:

if is_divisible(integer, divisor):

divisors.append(divisor)

divisor =- 1

return divisors

Continued

Page 26: Winpdb—A Platform Independent Python Debugger

if __name__ == '__main__':

# do some checking of the user's input

try:

if len(sys.argv) == 2:

# the following statement will raise a ValueError for

# non-integer arguments

test_integer = int(sys.argv[1])

# check to make sure the integer was positive

if test_integer <= 0:

raise ValueError("integer must be positive")

elif len(sys.argv) == 1:

# print the usage if there are no arguments and exit

print __doc__

sys.exit(0)

else:

# alert the user they did not provide the correct number of

# arguments

raise ValueError("too many arguments")

Continued

Page 27: Winpdb—A Platform Independent Python Debugger

# catch the errors raised if sys.argv[1] is not a positive integer

except ValueError, e:

# alert the user to provide a valid argument

print "Error: please provide one positive integer as an argument."

sys.exit(2)

divisors = find_divisors(test_integer)

# print the results

print "The divisors of %d are:" % test_integer

for divisor in divisors:

print divisor

But, when we run it,

E:\Some Folder>divisibles.py 100

The divisors of 100 are:

100

50

Page 28: Winpdb—A Platform Independent Python Debugger

So try the debugger

E:\Some Folder> winpdb divisibles.py 100

Click Next, to end of docstring (C to L)

Click Next, to import

Click Next, to definition of is_divisible

Click Next, hop over this definition (not executed until called) to definition of find_divisors

Click Next, hop over this definition to

if __name__ == '__main__':

Page 29: Winpdb—A Platform Independent Python Debugger

Click Next, into the try statement

Click Next, into the statement

if len(sys.argv) == 2:

Run the statements in there

Confirm test_integer is set to 100 (Look in Namespace)

Click Next, exit the try statement

Now at statement

divisors = find_divisors(test_integer)

Click Next, call find_divisors

Inspect result from this call, stored in the divisors value

Page 30: Winpdb—A Platform Independent Python Debugger

The Namespace frame might be getting a bit cluttered

The Filter button, 3rd from right, controls which variables will appear in the Namespace frame

By default, set to Medium

Clicking it toggles to Maximum, to Off, and back to Medium

Click Next to move to the statement after the call

In the Namespace frame, see that the value of variable divisors is a list

Expand it—see the values at the various indices This with Filter: Maximum

We’ve found a problem: this function returns a list with only 2 elements

But haven’t inspected the code in the function

Page 31: Winpdb—A Platform Independent Python Debugger

Stepping in Executed beyond where the problem lies

So restart, either

through the File menu (item Restart) or

the Console (type restart)

To confirm that the command line argument (100) was passed in, execute past the import sys statement

The sys module is then loaded

Evaluate sys.argv[1] in the command line

Console should show b’100’

Page 32: Winpdb—A Platform Independent Python Debugger

Put a breakpoint where the function is called

Now get to this line by either

clicking the Go button or

entering go (shortcut g) in the console

Page 33: Winpdb—A Platform Independent Python Debugger

The Step into button, or alternatively, the step command (shortcut s), lets us step into code in a called scope (e.g., function or method)

Can then run through that scope’s lines of code

Both Next and Step into execute 1 statement

But only Step into goes into a new scope

Click Step into

Now at function definition header, labeled “C”—just entered a new scope

Page 34: Winpdb—A Platform Independent Python Debugger

In the Namespace frame, the Locals and Globals are different Locals has just 1 entry: the

variable integer Globals has the old Locals

Click Next or Step into to complete entering the new scope and proceed to the 1st statement.

Click Next: new variable divisors, an empty list

Click Next: 50 in the list

Click Next 2x: new variable divisor with value 50

Execute into the loop until you get to the statement

if is_divisible(integer, divisor):

Page 35: Winpdb—A Platform Independent Python Debugger

Clicking Step into gets us to the declaration of is_divisible

The status is C

The formal parameters, with values, are in Locals

Note the stack

Page 36: Winpdb—A Platform Independent Python Debugger

Execute successive lines

On reaching the return, get an R (current scope about to return)

Execute through the return, and back to

divisors.append(divisor)

Next again, to

divisor =- 1

Here’s the error—should be: divisor -= 1

Page 37: Winpdb—A Platform Independent Python Debugger

Fix the error in the editor (e.g., Notepad++)

To check that we’ve fixed the script, restart the debugging session

Winpdb will re-execute as it was run previously, but with the updated code

Even retains any previous breakpoints set and command-line arguments

Restart the debugging session

Confirm there’s still a breakpoint on the line calling find_divisors

Click Go

Click Step into to get into the find_divisors scope

Step into the while loop and into is_divisible via the statement

if is_divisible(integer, divisor):

Page 38: Winpdb—A Platform Independent Python Debugger

Step through to make sure all is OK

It should return True (50 divides 100)

Return to the scope of find_divisors

Step until you reach the altered statement: divisor -= 1

Execute this statement

Check (in the Namespace frame) that it decremented correctly

Step through another iteration of the while loop, into the next call of is_divisible

100 isn’t divisible by 49

So go through the logic paths, making sure is_divisible returns False

Page 39: Winpdb—A Platform Independent Python Debugger

Step back to the scope of find_divisors

Confirm that

49 isn’t appended

divisor is properly decremented

Step back into the next call of is_divisible

Should be called with 100 and 48

Page 40: Winpdb—A Platform Independent Python Debugger

Return to Caller Now at the declaration of is_divisible with the status of C

Seen enough, but already entered the scope The Return button, or console

command return (or r), get us quickly out of a scope

Executes the scope’s code, goes directly to the return to the calling scope

Click Return

Click Next, return to the calling scope of find_divisors

Click Next to go through a few more rounds of the while loop without going through the is_divisible code

Page 41: Winpdb—A Platform Independent Python Debugger

When satisfied that the code is working, let the debugger handle the rest of the execution

Clicking Go lets the debuggee proceed to completion and exit

But, rather than the code executing cleanly,

an exception was raised

Click Yes

Page 42: Winpdb—A Platform Independent Python Debugger

Exceptional Analyses Winpdb goes into Exception Analysis mode, as evidenced in 2 ways:

the Analyze Exception button (right side of the control panel—‘e’ on a blue background) now appears activated, and

“Analyze mode was set to ON.” appears in the console

Exception Analysis mode takes us to the state when the exception was raised and left uncaught

Page 43: Winpdb—A Platform Independent Python Debugger

In the Exception tab in the Namespace frame, we see a ZeroDivisionError

The code frame shows a mod operation

Knowing the exception type, go back to the state of divisibles.py when it raised the ZeroDivisionError

Use the Stack data to help trace down what caused the exception

Page 44: Winpdb—A Platform Independent Python Debugger

Stack frames numbered and ordered top-down, most recent (and lowest number—0) at the top

Filenames also shown—so we can limit our attention

Click on a stack frame to see in the source browser the current statement (a call) in that frame

Clicking on the top frame reveals the offending statement in the code frame (the mod)

Page 45: Winpdb—A Platform Independent Python Debugger

The E next to this statement shows this to be the point where the ZeroDivisionError was raised

Page 46: Winpdb—A Platform Independent Python Debugger

The Locals tab in the Namespace frame shows that b’s value in this frame is 0

Recall: b is given assigned as an argument during the call of is_divisible

So this 0 originated at a less recent frame of the call stack

Look at frame 1, which is find_divisors

Page 47: Winpdb—A Platform Independent Python Debugger

See from the Locals tab in Namespace that divisor was set to 0 when passed to is_divisible

Odd: already fixed the bug that “decremented” divisor past 0

See whether we can repeat the error

Page 48: Winpdb—A Platform Independent Python Debugger

Breaking Under the Conditions Suspect the bug is hidden beneath a loop

Any breakpoint in that loop will stop execution upon every iteration

Need a breakpoint that only breaks execution when we want it to

A conditional breakpoint remains inactive (code continues past it) until some specified condition is reached

Then the breakpoint becomes active and halts execution at the line where it’s set

Specifying a condition involves specifying an expression

So we can set these breakpoints only from the console

Page 49: Winpdb—A Platform Independent Python Debugger

The console command bp sets a breakpoint

To set a conditional breakpoint at the console, enter

bp <line number>, <expression>

Set a conditional breakpoint to halt the while loop when divisor reaches 1

Then manually move through the code Restart

Look for the line number of the while loop statement:

while divisor >= 0:

and set a conditional breakpoint for when divisor reaches 1

bp 33, divisor == 1

Page 50: Winpdb—A Platform Independent Python Debugger

Previous breakpoint gets in the way—find and eliminate it

The console bl command lists the ids of breakpoints

The 1st breakpoint, on line 65, has an ID of 0

Clear this breakpoint using the bc command

Takes as an argument the ID of the breakpoint to be cleared

Clear the first breakpoint with

bc 0

Click Go

Page 51: Winpdb—A Platform Independent Python Debugger

Now at the if statement immediately within the while loop divisor should be 1

Step through

is_divisible returns True as it should (100 is divisible by 1)

divisor is decremented by 1

Return to the top of the loop

Click Step into to run the evaluation of the while loop condition

The body of the loop is executed again But it should stop once divisor reaches 0

Page 52: Winpdb—A Platform Independent Python Debugger

In the editor, change

while divisor >= 0

to

while divisor > 0

Restart the debugging session

Clear all breakpoints either

by going to the menu, and selecting in Breakpoints (the menu) Clear All or

by going to the Console and using the bc command

bc *

Click Go

Page 53: Winpdb—A Platform Independent Python Debugger

On the output terminal we see

The divisors of 100 are:

100

50

25

20

10

5

4

2

1