Pdb like a Pro

Master the basics and advanced usages of pdb to increase your productivity.

image by author

Pdb is most powerful debugger you can imagine and mastering it will revolutionize your development skills

If you liked pdb, you’ll love pdb++. The pdb++ debugger extends the basic functionalities of pdb with some nice to have features, the most important of which is the syntax highlighting. At the time of writing this post, the latest versio of pdb++ was 0.10.3 .

How to install pdb++?

You can install pdb++ via pip or conda package managers (the latest version at the time of this post was 0.10.3):

pip install pdbpp                   # install via pip
conda install -c conda-forge pdbpp # install via conda

Basics Usecases of pdb

Set Breakpoint

In Python 3.7+, a breakpoint can be set by calling the built-in breakpoint() function. The execution of the python module will pause at the breakpoint() line and pdb will be launched.

Disable All Breakpoints

Setting the PYTHONBREAKPOINT environment variable to 0 will disable all the breakpoints in the code, which is a lot easier than manually commenting them in the code. This is a sweet feature when disabling a handful of breakpoints during debugging.

python -c "breakpoint()"
# Will pause execution at the breakpoint and start the debugger

PYTHONBREAKPOINT=0 python -c "breakpoint()"
# Skips the breakpoint

Post-mortem Debugging

The pdbcan also be invoked as a script to debug other scripts, which will automatically enter the post-mortem debugging. With post-mortem debugging, pdb is invoked upon an uncaught exception. This is particularly helpful when debugging 3rd party libraries where inserting breakpoints in the source code is not as straightforward.

python -m pdb foo.py
python -m pdb -c continue foo.py
python -m foo.bar
python -m pdb -m foo.bar

Pdb and Pdb++ Commands

When pdb starts, the developer can start inspecting the state of the execution. To get the list of available commands, try help or h. Most commands can be abbreviated to one or two letters as indicated; e.g., h(elp) or cl(ear). Both forms are equivalent.

# foo/bar.py

def func3(sky, ocean, sea):
breakpoint()
print('func1', sky, ocean, sea)


def func2(sky, ocean):
# This is a long docstring to enable scrolling.
#
#
#
#
# end of docstring
func3(sky, ocean, 3)


def func1(sky):
func2(sky, 2)


func1(1)

Navigate Source Code

l[ist]: Lists 11 lines around the current line or continues the previous listing. The -> indicator points to where execution is paused. List is not an idempotent function and iteratively fetches the next 11 lines of the module.

✦ ❯ python foo/bar.py
[3] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(5)func3()
-> print('func1', sky, ocean, sea)
(Pdb++) l
1 # foo/bar.py
2
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)
6
7
8 def func2(sky, ocean):
9 # This is a long docstring to enable scrolling.
10 #
11 #

(Pdb++) l
12 #
13 #
14 # end of docstring
15 func3(sky, ocean, 3)
16
17
18 def func1(sky):
19 func2(sky, 2)
20
21
22 func1(1)

(Pdb++) l
[EOF]
(Pdb++) l.
1 # foo/bar.py
2
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)
6
7
8 def func2(sky, ocean):
9 # This is a long docstring to enable scrolling.
10 #
11 #
(Pdb++) ll
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)

(Pdb++) ll
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)
foo/my_class.py
(Pdb++) from foo.my_class import MyClass
(Pdb++) source MyClass
3 class MyClass:
4 """A delicate class indeed!"""
5 pass
(Pdb++) import numpy
(Pdb++) source numpy
0 """
1 NumPy
2 =====
3
4 Provides
5 1. An array object of arbitrary homogeneous items
6 2. Fast mathematical operations over arrays
7 3. Linear Algebra, Fourier Transforms, Random Number Generation
8
9 How to use the documentation
10 ----------------------------
11 Documentation is available in two forms: docstrings provided
12 with the code, and a loose standing reference guide, available from
13 `the NumPy homepage <https://www.scipy.org>`_.
14
15 We recommend exploring the docstrings using
16 `IPython <https://ipython.org>`_, an advanced Python shell with
17 TAB-completion and introspection capabilities. See below for further
18 instructions.

Exploring the Context

locals(): While this is not a pdbcommand, the built-in python locals() function is a helpful debugging tool to get the current local symbol table.

(Pdb++) ll
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)

(Pdb++) locals()
{'sky': 1, 'ocean': 2, 'sea': 3}
(Pdb++) locals()
{'sky': 1, 'ocean': 2, 'sea': 3}

(Pdb++) my_var = sum([v for k,v in locals().items()])
(Pdb++) locals()
{'sky': 1, 'ocean': 2, 'sea': 3, 'my_var': 6}
(Pdb++) sky
1

(Pdb++) sky?
Type: int
String Form: 1
Docstring: int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given. If x is a number, return x.__int__(). For floating point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base. The literal can be preceded by '+' or '-' and be surrounded
by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
(Pdb++) ll
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)

(Pdb++) args
sky = 1
ocean = 2
sea = 3

(Pdb++)
(Pdb++) {i:i for i in range(1000, 1020)}
{1000: 1000, 1001: 1001, 1002: 1002, 1003: 1003, 1004: 1004, 1005: 1005, 1006: 1006, 1007: 1007, 1008: 1008, 1009: 1009, 1010: 1010, 1011: 1011, 1012: 1012, 1013: 1013, 1014: 1014, 1015: 1015, 1016: 1016, 1017: 1017, 1018: 1018, 1019: 1019}

(Pdb++) pp {i:i for i in range(1000, 1020)}
{1000: 1000,
1001: 1001,
1002: 1002,
1003: 1003,
1004: 1004,
1005: 1005,
1006: 1006,
1007: 1007,
1008: 1008,
1009: 1009,
1010: 1010,
1011: 1011,
1012: 1012,
1013: 1013,
1014: 1014,
1015: 1015,
1016: 1016,
1017: 1017,
1018: 1018,
1019: 1019}
(Pdb++)

Moving between stacks

w[here]: Print a stack, with the most recent frame at the bottom. A > symbol indicates the current frame.

(Pdb++) w
[0] /Users/amirabdi/pdb_like_a_pro/foo/bar.py(22)<module>()
-> func1(1)
[1] /Users/amirabdi/pdb_like_a_pro/foo/bar.py(19)func1()
-> func2(sky, 2)
[2] /Users/amirabdi/pdb_like_a_pro/foo/bar.py(15)func2()
-> func3(sky, ocean, 3)
[3] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(5)func3()
-> print('func1', sky, ocean, sea)
(Pdb++) l .
1 # foo/bar.py
2
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)
6
7
8 def func2(sky, ocean):
9 func3(sky, ocean, 3)
10
11

(Pdb++) u
[2] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(9)func2()
-> func3(sky, ocean, 3)

(Pdb++) l .
4 breakpoint()
5 print('func1', sky, ocean, sea)
6
7
8 def func2(sky, ocean):
9 -> func3(sky, ocean, 3)
10
11
12 def func1(sky):
13 func2(sky, 2)
(Pdb++) l .
4 breakpoint()
5 print('func1', sky, ocean, sea)
6
7
8 def func2(sky, ocean):
9 -> func3(sky, ocean, 3)
10
11
12 def func1(sky):
13 func2(sky, 2)
14

(Pdb++) d
[3] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(5)func3()
-> print('func1', sky, ocean, sea)

(Pdb++) l .
1 # foo/bar.py
2
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)
6
7
8 def func2(sky, ocean):
9 func3(sky, ocean, 3)
(Pdb++) f 0
[0] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(16)<module>()
-> func1(1)
(Pdb++) l .
11
12 def func1(sky):
13 func2(sky, 2)
14
15
16 -> func1(1)
[EOF]

(Pdb++) f 3
[3] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(5)func3()
-> print('func1', sky, ocean, sea)
(Pdb++) l .
1 # foo/bar.py
2
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> print('func1', sky, ocean, sea)
6
7
8 def func2(sky, ocean):
9 func3(sky, ocean, 3)

Advance Breakpoints

You can set a breakpoint using the b[reakpoint] pdb command. It’s a very powerful tool; mastering the breakpoint skill gets you very far in life!

Breakpoint @ line

A breakpoint can be set at a line of the current module using the bcommand such as b <line_number>.

(Pdb++) b 9
Breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:9

(Pdb++) b 13
Breakpoint 2 at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:13

List all breakpoints

The b command without any arguments lists all breakpoints, excluding the breakpoints you hard-coded in the source code using breakpoint() .

(Pdb++) b
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:14
breakpoint already hit 1 time
2 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:9

Clear all breakpoints

The cl[ear] command deletes breakpoints:

  • cl : deletes all breakpoints from all modules
(Pdb++) b
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:14
breakpoint already hit 1 time
2 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:9

(Pdb++) cl
Clear all breaks? yes
Deleted breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:14
Deleted breakpoint 2 at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:9

(Pdb++) b
(Pdb++)
  • cl <breakpoint_number> : deletes the indicated breakpoint
(Pdb++) b
Num Type Disp Enb Where
3 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:14
4 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:9

(Pdb++) cl 3
Deleted breakpoint 3 at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:14

(Pdb++) b
Num Type Disp Enb Where
4 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:9

Breakpoint by module (file)

Breakpoints are set to the current module by default. To set a breakpoint on a different file, use the module path. For example,

(Pdb++) b /path/to/foo.py:10

Breakpoint by function

The b <function_name> command will set a breakpoint at the very first executable line of the given function. The function should have already been imported in the current scope and it can be from a 3rd party library.

# run in post-mortem to pause at the beginning
✦ ❯ python -m pdb foo/bar.py
[2] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(3)<module>()
-> def func3(sky, ocean, sea):

(Pdb++) l .
3 -> def func3(sky, ocean, sea):
4 print('func1', sky, ocean, sea)
5
6
7 def func2(sky, ocean):
8 func3(sky, ocean, 3)
9
10
11 def func1(sky):

(Pdb++) b func2
Breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/foo/bar.py:7

(Pdb++) cont
[4] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(8)func2()
-> func3(sky, ocean, 3)

(Pdb++) l .
3 def func3(sky, ocean, sea):
4 print('func1', sky, ocean, sea)
5
6
7 B def func2(sky, ocean):
8 -> func3(sky, ocean, 3)
9
10
11 def func1(sky):
12 func2(sky, 2)
# call.py
import requests
breakpoint()
requests.get('https://google.com')
✦ ❯ python call.py
[0] > /Users/amirabdi/pdb_like_a_pro/call.py(4)<module>()
-> requests.get('https://google.com')

(Pdb++) b requests.get
Breakpoint 1 at /Users/amirabdi/miniconda3/lib/python3.7/site-packages/requests/api.py:64

(Pdb++) cont
[1] > /Users/amirabdi/miniconda3/lib/python3.7/site-packages/requests/api.py(75)get()
-> kwargs.setdefault('allow_redirects', True)

(Pdb++) l
70 :param \*\*kwargs: Optional arguments that ``request`` takes.
71 :return: :class:`Response <Response>` object
72 :rtype: requests.Response
73 """
74
75 -> kwargs.setdefault('allow_redirects', True)
76 return request('get', url, params=params, **kwargs)
77
78
79 def options(url, **kwargs):
80 r"""Sends an OPTIONS request.

Breakpoint by condition

Conditional breakpoints are set with the b <where>, <condition> command and triggered only when the condition is met.

fibonnaci.py
# fibonnaci.py
def recur_fibo(value):
if value <= 1:
return value
else:
return(recur_fibo(value-1) + recur_fibo(value-2))

recur_fibo(10)
✦ ❯ python -m pdb fibonacci.py
[2] > /Users/amirabdi/pdb_like_a_pro/fibonacci.py(2)<module>()
-> def recur_fibo(value):

(Pdb++) b recur_fibo, value == 5
Breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:2

(Pdb++) b
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:2
stop only if value == 5
✦ ❯ python -m pdb fibonacci.py
[2] > /Users/amirabdi/pdb_like_a_pro/fibonacci.py(2)<module>()
-> def recur_fibo(value):

(Pdb++) b recur_fibo, value == 5
Breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:2

(Pdb++) b
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:2
stop only if value == 5

(Pdb++) cont
[8] > /Users/amirabdi/pdb_like_a_pro/fibonacci.py(3)recur_fibo()
-> if value <= 1:

(Pdb++) l .
1 # fibonnaci.py
2 B def recur_fibo(value):
3 -> if value <= 1:
4 return value
5 else:
6 return(recur_fibo(value-1) + recur_fibo(value-2))
7
8 recur_fibo(10)
[EOF]

(Pdb++) value
5

Temporary Breakpoint (tbreak)

A temporary breakpoint is set with the tbreak command and is breaks the execution only once.

~/pdb_like_a_pro via 🐍 v3.7.4 via C base on ☁️  us-west-2 took 3m37s
✦ ❯ python -m pdb fibonacci.py
[2] > /Users/amirabdi/pdb_like_a_pro/fibonacci.py(2)<module>()
-> def recur_fibo(value):

(Pdb++) tbreak recur_fibo
Breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:2

(Pdb++) cont
Deleted breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:2
[3] > /Users/amirabdi/pdb_like_a_pro/fibonacci.py(3)recur_fibo()
-> if value <= 1:

(Pdb++) value
10

(Pdb++) cont
The program finished and will be restarted
(Pdb++) tbreak 5
Breakpoint 1 at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:5
(Pdb++) break 6
Breakpoint 2 at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:6

(Pdb++) b
Num Type Disp Enb Where
1 breakpoint del yes at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:5
2 breakpoint keep yes at /Users/amirabdi/pdb_like_a_pro/fibonacci.py:6

Code Execution

The following pdb commands can be used to control the execution of the code: step, next, until, return, continue, and jump. Here is a quick summary of the most useful ones, step, next, return, and until.

s[tep] vs n[ext]: `step` steps into the function calls, while `next` steps over them.

step (step into): The step command executes the current line and steps into a called function or stops on the next line in the current function.

(Pdb++) l .
6
7 def func2(sky, ocean):
8 x = 10
9 y = x
10 breakpoint()
11 -> func3(sky, ocean, 3)
12 z = y
13 alpha = sum([x, y, z])
14
15
16 def func1(sky):

(Pdb++) s
--Call--
[5] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(3)func3()
-> def func3(sky, ocean, sea):

(Pdb++) l .
1 # foo/bar.py
2
3 -> def func3(sky, ocean, sea):
4 print('func1', sky, ocean, sea)
5
6
7 def func2(sky, ocean):
8 x = 10
9 y = x
10 breakpoint()
11 func3(sky, ocean, 3)
  • next(step over): The next command continues the execution until the next line in the current function is reached, so, it steps over function calls.
(Pdb++) l .
6
7 def func2(sky, ocean):
8 x = 10
9 y = x
10 breakpoint()
11 -> func3(sky, ocean, 3)
12 z = y
13 alpha = sum([x, y, z])
14
15
16 def func1(sky):

(Pdb++) n
func1 1 2 3
[4] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(12)func2()
-> z = y

(Pdb++) l .
7 def func2(sky, ocean):
8 x = 10
9 y = x
10 breakpoint()
11 func3(sky, ocean, 3)
12 -> z = y
13 alpha = sum([x, y, z])
14
15
16 def func1(sky):
17 func2(sky, 2)
✦ ❯ python -m foo.bar
[5] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(5)func3()
-> x = 10

(Pdb++) l .
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> x = 10
6 y = x
7 z = y
8 alpha = sum([x, y, z])
9 print('func1', sky, ocean, sea)
10

(Pdb++) return
func1 1 2 3
--Return--
[5] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(9)func3()->None
-> print('func1', sky, ocean, sea)

(Pdb++) l .
4 breakpoint()
5 x = 10
6 y = x
7 z = y
8 alpha = sum([x, y, z])
9 -> print('func1', sky, ocean, sea)
10
11
12 def func2(sky, ocean):
13 func3(sky, ocean, 3)
14
(Pdb++) l .
1 # foo/bar.py
2
3 def func3(sky, ocean, sea):
4 breakpoint()
5 -> x = 10
6 y = x
7 z = y
8 alpha = sum([x, y, z])
9 print('func1', sky, ocean, sea)
10
11

(Pdb++) until 8
[5] > /Users/amirabdi/pdb_like_a_pro/foo/bar.py(8)func3()
-> alpha = sum([x, y, z])

(Pdb++) l .
3 def func3(sky, ocean, sea):
4 breakpoint()
5 x = 10
6 y = x
7 z = y
8 -> alpha = sum([x, y, z])
9 print('func1', sky, ocean, sea)
10
11
12 def func2(sky, ocean):
13 func3(sky, ocean, 3)

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store