📜 ⬆️ ⬇️

pdb - Interactive debugger

pdb - Interactive debugger


Python versions: 1.4 and higher

pdb is an interactive debugging environment for Python programs. It includes the ability to pause program execution, view variable values, line-by-line code execution so that you can understand what your program actually does and find logical errors.

Running debugger

To start using pdb, you need to tell the interpreter how and when we want to see the debugger. There are several ways to do this, depending on the launch conditions, as well as debugging requirements.

From command line

The most obvious method of using a debugger is to run it from the command line, passing your program as a parameter so that it knows what to run.
# encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  1. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  2. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  3. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  4. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  5. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  6. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  7. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  8. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  9. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  10. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  11. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  12. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
  13. # encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .
# encoding: utf-8 # # Copyright (c) 2010 Doug Hellmann. All rights reserved. # class MyObj( object ): def __init__(self, num_loops): self.count = num_loops def go(self): for i in range(self.count): print i return if __name__ == '__main__' : MyObj(5).go() * This source code was highlighted with Source Code Highlighter .

When you start the debugger from the command line, it loads your source code and stops execution on the first expression found. In our case, it will stop before performing the definition of the class MyObj in the seventh line.
')
$ python -m pdb pdb_script.py
> .../pdb_script.py(7)<module>()
-> class MyObj(object):
(Pdb)

Note:
Usually pdb includes the full path to each module when displaying the file path. For the sake of brevity of examples in the debugger output, these paths are replaced by ...

From the interpreter

Many developers work with an interactive interpreter in the early stages of developing modules, as this allows them to experiment more iteratively without having to repeat the save / run / repeat loop, which is necessary when creating stand-alone scripts. To run the debugger from an interactive session, use run () or runeval ().

$ python
Python 2.7 (r27:82508, Jul 3 2010, 21:12:11)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb_script
>>> import pdb
>>> pdb.run('pdb_script.MyObj(5).go()')
> <string>(1)<module>()
(Pdb)


The run () argument is a string expression that can be executed by the Python interpreter. The debugger parses it and stops execution just before running the first expression. You can use the commands described below to navigate and control the execution.

From your program

Both of the previous examples were based on the fact that you want to start the debugger at the very beginning of your program. For more complex processes in which the problem occurs much later, at run time, it is more convenient to start the debugger from within the program using set_trace ().
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. import pdb
  7. class MyObj ( object ):
  8. def __init __ (self, num_loops):
  9. self.count = num_loops
  10. def go (self):
  11. for i in range (self.count):
  12. pdb.set_trace ()
  13. print i
  14. return
  15. if __name__ == '__main__' :
  16. MyObj (5) .go ()

In line 16, the test script invokes the debugger.

$ python ./pdb_set_trace.py
> .../pdb_set_trace.py(17)go()
-> print i
(Pdb)


set_trace () is just a function in Python, so it can be called at any time during program execution. This allows you to get into debug mode, depending on the conditions prevailing within the program, for example, when handling exceptions, or in a special branch of a control expression

After failure

Debugging a program after its completion is called post-mortem debugging. pdb supports this debugging using the pm () and post_mortem () functions.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. class MyObj ( object ):
  7. def __init __ (self, num_loops):
  8. self.count = num_loops
  9. def go (self):
  10. for i in range (self.num_loops):
  11. print i
  12. return

Here, on the 13th line, an incorrect field name will cause an AttributeError exception, this will stop execution. pm () looks at interactive traceback and runs the debugger in the stack frame where the exception occurred.

$ python
Python 2.7 (r27:82508, Jul 3 2010, 21:12:11)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from pdb_post_mortem import MyObj
>>> MyObj(5).go()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pdb_post_mortem.py", line 13, in go
for i in range(self.num_loops):
AttributeError: 'MyObj' object has no attribute 'num_loops'
>>> import pdb
>>> pdb.pm()
> .../pdb_post_mortem.py(13)go()
-> for i in range(self.num_loops):
(Pdb)


Debugger Management

Interaction with the debugger is reduced to a small command language that allows you to navigate the stack, view and change the values ​​of variables, control how the debugger will execute your program. The debugger uses readline to read commands. Entering an empty line repeats the previous command, except when viewing the source.

Call Stack Navigation

At any time during the debugger, you can use where (or simply w ) to clarify which line is currently running and where you are on the call stack. In this case, the pdb_set_trace.py module is line 17 in the go () method.

$ python pdb_set_trace.py
> .../pdb_set_trace.py(17)go()
-> print i
(Pdb) where
.../pdb_set_trace.py(21)<module>()
-> MyObj(5).go()
> .../pdb_set_trace.py(17)go()
-> print i


To view the context of the current location, use list ( l ).

(Pdb) list
12 self.count = num_loops
13
14 def go(self):
15 for i in range(self.count):
16 pdb.set_trace()
17 -> print i
18 return
19
20 if __name__ == '__main__':
21 MyObj(5).go()
[EOF]
(Pdb)


By default, 11 lines are displayed around the current one (5 above and 5 below). When calling list with a single argument, it will output 11 lines adjacent to the given one, instead of the current one.

(Pdb) list 14
9 class MyObj(object):
10
11 def __init__(self, num_loops):
12 self.count = num_loops
13
14 def go(self):
15 for i in range(self.count):
16 pdb.set_trace()
17 -> print i
18 return
19


If you pass two arguments to the list, they will be processed as the first and last lines that will be displayed.

(Pdb) list 5, 19
5 #
6
7 import pdb
8
9 class MyObj(object):
10
11 def __init__(self, num_loops):
12 self.count = num_loops
13
14 def go(self):
15 for i in range(self.count):
16 pdb.set_trace()
17 -> print i
18 return
19


Navigate the call stack using up and down . up (shortly u ) moves to earlier calls on the stack. down (or d ) transfers to deeper calls.

(Pdb) up
> .../pdb_set_trace.py(21)<module>()
-> MyObj(5).go()

(Pdb) down
> .../pdb_set_trace.py(17)go()
-> print i


Each time you move through the stack, the debugger displays the current location in the same format as where.

View variables on the stack

A set of variables is associated with each frame of the stack, including values ​​local to the function being executed and information about the global state. pdb allows you to view the contents of these variables in several ways.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. import pdb
  7. def recursive_function (n = 5, output = 'to be printed' ):
  8. if n> 0:
  9. recursive_function (n-1)
  10. else :
  11. pdb.set_trace ()
  12. print output
  13. return
  14. if __name__ == '__main__' :
  15. recursive_function ()


The args command (also known as a ) displays all the arguments of the function that is active in the current frame. This example also uses the recursive function to show how the deep stack looks in the where output.

$ python pdb_function_arguments.py
> .../pdb_function_arguments.py(14)recursive_function()
-> return
(Pdb) where
.../pdb_function_arguments.py(17)<module>()
-> recursive_function()
.../pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
.../pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
.../pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
.../pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
.../pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
> .../pdb_function_arguments.py(14)recursive_function()
-> return

(Pdb) args
n = 0
output = to be printed

(Pdb) up
> .../pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)

(Pdb) args
n = 1
output = to be printed

(Pdb)


The p command executes the expression given by the argument and prints the result. You can also use the print statement, but it is passed to the Python interpreter for execution, instead of being executed, as a debugger command.

(Pdb) pn
1

(Pdb) print n
1


Similarly, starting the expression with ! you immediately send it to the Python interpreter for execution. This feature can be used to execute arbitrary Python commands, including changing variables. In this example, the output value is changed before allowing the debugger to continue executing the program. The next call, after set_trace (), outputs output, displaying the modified value.

$ python pdb_function_arguments.py
> .../pdb_function_arguments.py(14)recursive_function()
-> print output

(Pdb) !output
'to be printed'

(Pdb) !output='changed value'

(Pdb) continue
changed value


For more complex values, such as nested or large data structures, use pp (pretty print) for “nice” output. This program reads several lines of text from a file.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. import pdb
  7. with open ( 'lorem.txt' , 'rt' ) as f:
  8. lines = f.readlines ()
  9. pdb.set_trace ()

Outputting lines with p is difficult to read, because he is transferred unnaturally. pp uses pprint to format values ​​before output.

$ python pdb_pp.py
--Return--
> .../pdb_pp.py(12)<module>()->None
-> pdb.set_trace()
(Pdb) p lines
['Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\n', 'egestas, enim
et consectetuer ullamcorper, lectus ligula rutrum leo, a\n', 'elementum elit tortor
eu quam.\n']

(Pdb) pp lines
['Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\n',
'egestas, enim et consectetuer ullamcorper, lectus ligula rutrum leo, a\n',
'elementum elit tortor eu quam.\n']

(Pdb)


Pacing on the program

In addition to navigating up and down the stack while the program is paused, you can also continue execution step by step from where the debugger is invoked.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. import pdb
  7. def f (n):
  8. for i in range (n):
  9. j = i * n
  10. print i, j
  11. return
  12. if __name__ == '__main__' :
  13. pdb.set_trace ()
  14. f (5)

Use step to execute the current line and stop before the next executable point — either the first expression inside the function being called, or the next line in the current function.

$ python pdb_step.py
> /Users/dhellmann/Documents/PyMOTW/src.pdb/PyMOTW/pdb/pdb_step.py(17)<module>()
-> f(5)


The interpreter stops at the call to set_trace () and transfers control to the debugger. As a result of the first step call, the f () call is made.

(Pdb) step
--Call--
> .../pdb_step.py(9)f()
-> def f(n):


Once again step, and the current line becomes the first in the f () function and starts the loop.

(Pdb) step
> .../pdb_step.py(10)f()
-> for i in range(n):


Stepping over again, we get to the first line inside the loop, where j is defined.

(Pdb) step
> /Users/dhellmann/Documents/PyMOTW/src.pdb/PyMOTW/pdb/pdb_step.py(11)f()
-> j = i * n
(Pdb) pi
0


The value of i is 0, so after this step the value of j must also be 0.

(Pdb) step
> /Users/dhellmann/Documents/PyMOTW/src.pdb/PyMOTW/pdb/pdb_step.py(12)f()
-> print i, j

(Pdb) pj
0

(Pdb)


Stepping over again and again can be tedious if you have to overcome a lot of code to the point of error, or if the function is re-called.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. import pdb
  7. def calc (i, n):
  8. j = i * n
  9. return j
  10. def f (n):
  11. for i in range (n):
  12. j = calc (i, n)
  13. print i, j
  14. return
  15. if __name__ == '__main__' :
  16. pdb.set_trace ()
  17. f (5)

In this example, there are no problems with calc (), so going through all its lines, with each call in a cycle in f (), only clogs the useful output, displaying all the lines of calc () as they are executed.

$ python pdb_next.py
> .../pdb_next.py(21)<module>()
-> f(5)
(Pdb) step
--Call--
> .../pdb_next.py(13)f()
-> def f(n):

(Pdb) step
> .../pdb_next.py(14)f()
-> for i in range(n):

(Pdb) step
> .../pdb_next.py(15)f()
-> j = calc(i, n)

(Pdb) step
--Call--
> .../pdb_next.py(9)calc()
-> def calc(i, n):

(Pdb) step
> .../pdb_next.py(10)calc()
-> j = i * n

(Pdb) step
> .../pdb_next.py(11)calc()
-> return j

(Pdb) step
--Return--
> .../pdb_next.py(11)calc()->0
-> return j

(Pdb) step
> .../pdb_next.py(16)f()
-> print i, j

(Pdb) step
0 0


The next command is similar to step, but is not included in the functions called by the current expression. As a result, after executing this command, the debugger proceeds to the next expression of the current function.

> .../pdb_next.py(14)f()
-> for i in range(n):
(Pdb) step
> .../pdb_next.py(15)f()
-> j = calc(i, n)

(Pdb) next
> .../pdb_next.py(16)f()
-> print i, j

(Pdb)


The u ntil command is like next, only it continues execution until it reaches the line of the current function located below (with a number higher than that of the current one). This means that, for example, until can be used to exit loops.

$ python pdb_next.py
> .../pdb_next.py(21)<module>()
-> f(5)
(Pdb) step
--Call--
> .../pdb_next.py(13)f()
-> def f(n):

(Pdb) step
> .../pdb_next.py(14)f()
-> for i in range(n):

(Pdb) step
> .../pdb_next.py(15)f()
-> j = calc(i, n)

(Pdb) next
> .../pdb_next.py(16)f()
-> print i, j

(Pdb) until
0 0
1 5
2 10
3 15
4 20
> .../pdb_next.py(17)f()
-> return

(Pdb)


Before the execution of until, the current line was 16 - the last line of the loop. After the start of until, the execution reached the 17th line and the cycle was exhausted.

return is another way to skip parts of a function. It continues execution until the moment when the return from the current function is to be performed, and suspends the program. This gives time to look at the return value before returning from a function.

$ python pdb_next.py
> .../pdb_next.py(21)<module>()
-> f(5)
(Pdb) step
--Call--
> .../pdb_next.py(13)f()
-> def f(n):

(Pdb) step
> .../pdb_next.py(14)f()
-> for i in range(n):

(Pdb) return
0 0
1 5
2 10
3 15
4 20
--Return--
> .../pdb_next.py(17)f()->None
-> return

(Pdb)


Stopping points

When programs get even bigger, even using next and until will become slow and boring. Instead of line-by-step stepping through the program, there is a better solution — letting it run normally until it reaches the point where you need to interrupt it. You can use set_trace () to start the debugger, but this only works if you want to stop the program in one place. A more convenient solution is to run the entire program in the debugger, but tell him in advance where to stop using breakpoints. The debugger monitors the execution of the program, and when it reaches the location described by the breakpoint, it stops execution in front of the designated line.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. def calc (i, n):
  7. j = i * n
  8. print 'j =' , j
  9. if j> 0:
  10. print 'Positive!'
  11. return j
  12. def f (n):
  13. for i in range (n):
  14. print 'i =' , i
  15. j = calc (i, n)
  16. return
  17. if __name__ == '__main__' :
  18. f (5)

The break command accepts several options for setting breakpoints. You can specify the line number, file, or function where the execution should stop. To set a breakpoint on a specific line in the current file, use break lineno :

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break 11
Breakpoint 1 at .../pdb_break.py:11

(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb_break.py(11)calc()
-> print 'Positive!'

(Pdb)

The continue command informs the debugger about the continuation of the program execution until the next breakpoint. In this case, the first iteration of the loop in f () will pass, and stop in calc () at the second iteration.

Breakpoints can also be set to the first line of the function if you specify its name instead of the line number. The following example will show what happens if you add a breakpoint to the calc () function.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:7

(Pdb) continue
i = 0
> .../pdb_break.py(8)calc()
-> j = i * n

(Pdb) where
.../pdb_break.py(21)<module>()
-> f(5)
.../pdb_break.py(17)f()
-> j = calc(i, n)
> .../pdb_break.py(8)calc()
-> j = i * n

(Pdb)


To specify a breakpoint in another file, precede the line number, or the function name with the file name.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. from pdb_break import f
  7. f (5)

Here we set the breakpoint on the 11th line of the pdb_break.py after the launch of the main program pdb_break_remote.py.

$ python -m pdb pdb_break_remote.py
> .../pdb_break_remote.py(7)<module>()
-> from pdb_break import f
(Pdb) break pdb_break.py:11
Breakpoint 1 at .../pdb_break.py:11

(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb_break.py(11)calc()
-> print 'Positive!'

(Pdb)


The file name can be the full path to the source, or a relative path to the file available in sys.path.

To view the currently set breakpoints, use break with no arguments. The output includes the file and line number of each breakpoint, as well as information about how many times it has been hit.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break 11
Breakpoint 1 at .../pdb_break.py:11

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:11

(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb/pdb_break.py(11)calc()
-> print 'Positive!'

(Pdb) continue
Positive!
i = 2
j = 10
> .../pdb_break.py(11)calc()
-> print 'Positive!'

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:11
breakpoint already hit 2 times

(Pdb)


Breakpoint management

Each added breakpoint is assigned a numeric identifier. These identifiers are used to interactively enable, disable, and delete breakpoint.

Turning off breakpoint with the help disable tells the debugger not to stop when this line is reached. Breypoint is remembered, but ignored.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:7

(Pdb) break 11
Breakpoint 2 at .../pdb_break.py:11

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:7
2 breakpoint keep yes at .../pdb_break.py:11

(Pdb) disable 1

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep no at .../pdb_break.py:7
2 breakpoint keep yes at .../pdb_break.py:11

(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb_break.py(11)calc()
-> print 'Positive!'

(Pdb)


The debugging session from the following example installs two breakpoints in the program, then one of them disables. The program is executed until the remaining breakpoint is reached, then another breakpoint is turned on and execution continues.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:7

(Pdb) break 16
Breakpoint 2 at .../pdb_break.py:16

(Pdb) disable 1

(Pdb) continue
> .../pdb_break.py(16)f()
-> print 'i =', i

(Pdb) list
11 print 'Positive!'
12 return j
13
14 def f(n):
15 for i in range(n):
16 B-> print 'i =', i
17 j = calc(i, n)
18 return
19
20 if __name__ == '__main__':
21 f(5)

(Pdb) continue
i = 0
j = 0
> .../pdb_break.py(16)f()
-> print 'i =', i

(Pdb) list
11 print 'Positive!'
12 return j
13
14 def f(n):
15 for i in range(n):
16 B-> print 'i =', i
17 j = calc(i, n)
18 return
19
20 if __name__ == '__main__':
21 f(5)

(Pdb) pi
1

(Pdb) enable 1

(Pdb) continue
i = 1
> .../pdb_break.py(8)calc()
-> j = i * n

(Pdb) list
3 #
4 # Copyright (c) 2010 Doug Hellmann. All rights reserved.
5 #
6
7 B def calc(i, n):
8 -> j = i * n
9 print 'j =', j
10 if j > 0:
11 print 'Positive!'
12 return j
13

(Pdb)


The lines before which B is indicated in the output of the list show the program places where the breakpoint is set (lines 9 and 18).

Use clear to completely remove breakpoint.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:7

(Pdb) break 11
Breakpoint 2 at .../pdb_break.py:11

(Pdb) break 16
Breakpoint 3 at .../pdb_break.py:16

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:7
2 breakpoint keep yes at .../pdb_break.py:11
3 breakpoint keep yes at .../pdb_break.py:16

(Pdb) clear 2
Deleted breakpoint 2

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:7
3 breakpoint keep yes at .../pdb_break.py:16

(Pdb)


The remaining breypoint will retain their identifiers and are not renumbered.

Temporary breakpoints

Temporary breakpoints are automatically cleared after the first hit. Using a temporary breakpoint, you can quickly reach a certain point of program execution, as with a normal breakpoint, however, since it is immediately cleared, it does not interfere with further work, if this piece of the program will be re-executed.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) tbreak 11
Breakpoint 1 at .../pdb_break.py:11

(Pdb) continue
i = 0
j = 0
i = 1
j = 5
Deleted breakpoint 1
> .../pdb_break.py(11)calc()
-> print 'Positive!'

(Pdb) break

(Pdb) continue
Positive!
i = 2
j = 10
Positive!
i = 3
j = 15
Positive!
i = 4
j = 20
Positive!
The program finished and will be restarted
> .../pdb_break.py(7)<module>()
-> def calc(i, n):

(Pdb)


After the execution has reached the 11th row for the first time, the breakpoint is removed and the program runs non-stop to the end.

Conditional breakpoints

Rules can also be applied to breakpoints so that program execution will stop only if conditions are met. Using conditional breakpoints allows for finer control of stops than manual switching on and off of breakpoints.

Conditional breakpoints can be set in one of two ways. The first is to specify a condition at the time of creating a breakpoint using break.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break 9, j>0
Breakpoint 1 at .../pdb_break.py:9

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:9
stop only if j>0

(Pdb) continue
i = 0
j = 0
i = 1
> .../pdb_break.py(9)calc()
-> print 'j =', j

(Pdb)


The condition argument must be an expression using variable names, visible in the stack frame in which the breakpoint is defined. If the expression is reduced to the value True, then the program execution stops at breakpoint.

The condition can also be applied to an existing breakpoint using the condition command. The argument is a breakpoint id and an expression.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break 9
Breakpoint 1 at .../pdb_break.py:9

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:9

(Pdb) condition 1 j>0

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:9
stop only if j>0

(Pdb)


Ignore breakpoints

Programs with a large number of cycles or recursive calls are often easier to debug by “scrolling” through certain stages of the program, instead of viewing each call and breakpoint. The ignore command tells the debugger to skip some breakpoint. With each breakpoint passing, it decreases the ignore counter. When it reaches 0, breakpoint is activated again.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break 17
Breakpoint 1 at .../pdb_break.py:17

(Pdb) continue
i = 0
> .../pdb_break.py(17)f()
-> j = calc(i, n)

(Pdb) next
j = 0
> .../pdb_break.py(15)f()
-> for i in range(n):

(Pdb) ignore 1 2
Will ignore next 2 crossings of breakpoint 1.

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:17
ignore next 2 hits
breakpoint already hit 1 time

(Pdb) continue
i = 1
j = 5
Positive!
i = 2
j = 10
Positive!
i = 3
> .../pdb_break.py(17)f()
-> j = calc(i, n)

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:17
breakpoint already hit 4 times


Explicitly setting the counter to zero immediately activates the breakpoint.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break 17
Breakpoint 1 at .../pdb_break.py:17

(Pdb) ignore 1 2
Will ignore next 2 crossings of breakpoint 1.

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:17
ignore next 2 hits

(Pdb) ignore 1 0
Will stop next time breakpoint 1 is reached.

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:17


Starting breakpoint operations

In addition to the interactive mode, pdb supports simple scripting. Using commands, you can determine the sequence of interpreter actions, including Python expressions, that will be executed when it hits any breakpoint. After the commands are executed with the line number argument, the debugger prompt changes to (com). Enter the commands one at a time, and at the end enter end to save the script and return to the main debugger mode.

$ python -m pdb pdb_break.py
> .../pdb_break.py(7)<module>()
-> def calc(i, n):
(Pdb) break 9
Breakpoint 1 at .../pdb_break.py:9

(Pdb) commands 1
(com) print 'debug i =', i
(com) print 'debug j =', j
(com) print 'debug n =', n
(com) end

(Pdb) continue
i = 0
debug i = 0
debug j = 0
debug n = 5
> .../pdb_break.py(9)calc()
-> print 'j =', j

(Pdb) continue
j = 0
i = 1
debug i = 1
debug j = 5
debug n = 5
> .../pdb_break.py(9)calc()
-> print 'j =', j

(Pdb)


This function is especially useful for debugging code that uses many data structures, variables, because allows you to output all values ​​automatically, instead of doing it manually on each breakpoint.

Manage progress

The jump command allows you to control program execution on the fly without changing the source code. You can skip part of the program forward, avoiding the execution of some code, or backward, to re-execute it. A simple program to generate a list of numbers.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. def f (n):
  7. result = []
  8. j = 0
  9. for i in range (n):
  10. j = i * n + j
  11. j + = n
  12. result.append (j)
  13. return result
  14. if __name__ == '__main__' :
  15. print f (5)

When started without intervention, the output is an increasing sequence of numbers divisible by 5.

$ python pdb_jump.py

[5, 15, 30, 50, 75]


Go forward

Going forward transfers the execution to a point, then the current line, without executing the commands located before it. Skipping the 13th line in the example below, the value of j is not incremented and all subsequent values ​​that depend on it become slightly smaller.

$ python -m pdb pdb_jump.py
> .../pdb_jump.py(7)<module>()
-> def f(n):
(Pdb) break 12
Breakpoint 1 at .../pdb_jump.py:12

(Pdb) continue
> .../pdb_jump.py(12)f()
-> j += n

(Pdb) pj
0

(Pdb) step
> .../pdb_jump.py(13)f()
-> result.append(j)

(Pdb) pj
5

(Pdb) continue
> .../pdb_jump.py(12)f()
-> j += n

(Pdb) jump 13
> .../pdb_jump.py(13)f()
-> result.append(j)

(Pdb) pj
10

(Pdb) disable 1

(Pdb) continue
[5, 10, 25, 45, 70]

The program finished and will be restarted
> .../pdb_jump.py(7)<module>()
-> def f(n):
(Pdb)


Go back

Jumps can also transfer program execution to expressions that have already been executed to restart. Here the value of j is incremented once again so that as a result, the numbers go out more than they would otherwise.

$ python -m pdb pdb_jump.py
> .../pdb_jump.py(7)<module>()
-> def f(n):
(Pdb) break 13
Breakpoint 1 at .../pdb_jump.py:13

(Pdb) continue
> .../pdb_jump.py(13)f()
-> result.append(j)

(Pdb) pj
5

(Pdb) jump 12
> .../pdb_jump.py(12)f()
-> j += n

(Pdb) continue
> .../pdb_jump.py(13)f()
-> result.append(j)

(Pdb) pj
10

(Pdb) disable 1

(Pdb) continue
[10, 20, 35, 55, 80]

The program finished and will be restarted
> .../pdb_jump.py(7)<module>()
-> def f(n):
(Pdb)

Forbidden transitions

Transitions "in" and "from" some expressions that control the flow of calculations are dangerous or ambiguous, and, therefore, prohibited by the debugger.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. def f (n):
  7. if n <0:
  8. raise ValueError ( 'Invalid n:% s' % n)
  9. result = []
  10. j = 0
  11. for i in range (n):
  12. j = i * n + j
  13. j + = n
  14. result.append (j)
  15. return result
  16. if __name__ == '__main__' :
  17. try :
  18. print f (5)
  19. finally :
  20. print 'Always printed'
  21. try :
  22. print f (-5)
  23. except:
  24. print 'There was an error'
  25. else :
  26. print 'There was no error'
  27. print 'Last statement'

You can go inside the function, but if you do, the arguments will not be defined and the code is unlikely to work.

$ python -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(7)<module>()
-> def f(n):
(Pdb) break 21
Breakpoint 1 at .../pdb_no_jump.py:21

(Pdb) jump 8
> .../pdb_no_jump.py(8)<module>()
-> if n < 0:

(Pdb) pn
*** NameError: NameError("name 'n' is not defined",)

(Pdb) args

(Pdb)


You cannot go inside blocks such as for loops or a try: except statement.

$ python -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(7)<module>()
-> def f(n):
(Pdb) break 21
Breakpoint 1 at .../pdb_no_jump.py:21

(Pdb) continue
> .../pdb_no_jump.py(21)<module>()
-> print f(5)

(Pdb) jump 26
*** Jump failed: can't jump into the middle of a block

(Pdb)


The code in the finally block must be executed without fail, so you cannot jump out of it.
$ python -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(7)<module>()
-> def f(n):
(Pdb) break 23
Breakpoint 1 at .../pdb_no_jump.py:23

(Pdb) continue
[5, 15, 30, 50, 75]
> .../pdb_no_jump.py(23)<module>()
-> print 'Always printed'

(Pdb) jump 25
*** Jump failed: can't jump into or out of a 'finally' block

(Pdb)


And the main limitation - the transitions are limited to the lower frame of the stack. If you go up the stack to view variables, you cannot change the progress in this level.

$ python -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(7)<module>()
-> def f(n):
(Pdb) break 11
Breakpoint 1 at .../pdb_no_jump.py:11

(Pdb) continue
> .../pdb_no_jump.py(11)f()
-> j = 0

(Pdb) where
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/bdb.py(379)run()
-> exec cmd in globals, locals
<string>(1)<module>()
.../pdb_no_jump.py(21)<module>()
-> print f(5)
> .../pdb_no_jump.py(11)f()
-> j = 0

(Pdb) up
> .../pdb_no_jump.py(21)<module>()
-> print f(5)

(Pdb) jump 25
*** You can only jump within the bottom frame

(Pdb)


Restart the program

When the debugger reaches the end of the program, it automatically starts it first. You can also restart it yourself, without leaving the debug mode, and without losing breakpoints or other settings.
  1. #! / usr / bin / env python
  2. # encoding: utf-8
  3. #
  4. # Copyright (c) 2010 Doug Hellmann. All rights reserved.
  5. #
  6. import sys
  7. def f ():
  8. print 'Command line args:' , sys.argv
  9. return
  10. if __name__ == '__main__' :
  11. f ()

Running the above program in the debugger will print the name of the script, since no other arguments were given on the command line.

$ python -m pdb pdb_run.py
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb) continue

Command line args: ['pdb_run.py']
The program finished and will be restarted
> .../pdb_run.py(7)<module>()
-> import sys

(Pdb)


You can restart the program using run . The run arguments are parsed with shlex, and passed to the program as if they were from the command line, so you can restart your program with different parameters.

(Pdb) run abc "this is a long value"
Restarting pdb_run.py with arguments:
abc this is a long value
> .../pdb_run.py(7)<module>()
-> import sys

(Pdb) continue
Command line args: ['pdb_run.py', 'a', 'b', 'c', 'this is a long value']
The program finished and will be restarted
> .../pdb_run.py(7)<module>()
-> import sys

(Pdb)


run can also be used elsewhere to restart the program.

$ python -m pdb pdb_run.py
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb) break 10
Breakpoint 1 at .../pdb_run.py:10

(Pdb) continue
> .../pdb_run.py(10)f()
-> print 'Command line args:', sys.argv

(Pdb) run one two three
Restarting pdb_run.py with arguments:
one two three
> .../pdb_run.py(7)<module>()
-> import sys

(Pdb)


Configuring the debugger using aliases

There is a way to avoid re-typing complex commands - to define an alias. The alias expansion is applied to the first word of each command. The alias body can contain any command allowed by the debugger interpreter, including debugger commands and Python expressions. In the definitions of aliases recursion is allowed, so that some aliases can cause others.

$ python -m pdb pdb_function_arguments.py
> .../pdb_function_arguments.py(7)<module>()
-> import pdb
(Pdb) break 10
Breakpoint 1 at .../pdb_function_arguments.py:10

(Pdb) continue
> .../pdb_function_arguments.py(10)recursive_function()
-> if n > 0:

(Pdb) pp locals().keys()
['output', 'n']

(Pdb) alias pl pp locals().keys()

(Pdb) pl
['output', 'n']


Running alias without arguments displays a list of defined aliases. With one argument - the name of the alias, its definition is displayed.

(Pdb) alias
pl = pp locals().keys()

(Pdb) alias pl
pl = pp locals().keys()
(Pdb)


The alias arguments can be accessed using% n, where n is the number of the argument, starting with 1. To refer to all the arguments, use % * .

$ python -m pdb pdb_function_arguments.py
> .../pdb_function_arguments.py(7)<module>()
-> import pdb
(Pdb) alias ph !help(%1)

(Pdb) ph locals
Help on built-in function locals in module __builtin__:

locals(...)
locals() -> dictionary

Update and return a dictionary containing the current scope's local variables.


Remove the alias definition with unalias .

(Pdb) unalias ph

(Pdb) ph locals
*** SyntaxError: invalid syntax (<stdin>, line 1)

(Pdb)


Saving configuration settings

Debugging a program requires a lot of repetition; running the code, analyzing the output, correcting the code and input, and running it again. pdb tries to cut the number of repetitions needed for the debugging process to allow you to concentrate on your code, not on the debugger. To help reduce the number of commands given to the debugger, pdb allows you to save the configuration, then read it again at startup.

First, the ~ / .pdbrc file is read, allowing you to specify personal preferences for all debugging sessions. After that, ./.pdbrc is read from the current directory so that you can specify settings for a specific project.

$ cat ~/.pdbrc
# Show python help
alias ph !help(%1)
# Overridden alias
alias redefined p 'home definition'

$ cat .pdbrc
# Breakpoints
break 10
# Overridden alias
alias redefined p 'local definition'

$ python -m pdb pdb_function_arguments.py
Breakpoint 1 at .../pdb_function_arguments.py:10
> .../pdb_function_arguments.py(7)<module>()
-> import pdb
(Pdb) alias
ph = !help(%1)
redefined = p 'local definition'

(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_function_arguments.py:10

(Pdb)


Any configuration commands that can be entered into the debugger shell can be stored in one of these files, but the execution control commands (continue, jump, etc.) cannot. The exception is run, that is, you can set the command line arguments for debugging sessions in ./.pdbrc so that they will remain unchanged throughout subsequent runs.

Source: https://habr.com/ru/post/104086/


All Articles