Debugging code is a necessity, but still I have seen many developers using print() statements instead of powerful
built-in pdb
python debugger.
In this tutorial, we will see how to use pdb for debugging Python3 scripts and complex application
code.
pdb can be used for debugging python code on all operating systems like Linux, Windows, MacOS etc. Hence, pdb
is very useful
for debugging python code in any kind of remote servers where we do not have access to GUI, therefore we can debug
our Python code
through commandline.
The most direct and the easiest way to use pdb is to import pdb and call set_trace() within your code on that particular line from where you want to start debugging.
Syntax:import pdb; pdb.set_trace()
If you are using Python 3.7+ , you can write breakpoint() in place of above syntax:
breakpoint()
Example:
def add_num(num1,num2):
print('we have reached inside add_num function')
new_num = num1 + num2
import pdb; pdb.set_trace()
return new_num
There is another way to debug code with pdb which is called Post-mortem debugging. Let's
assume you want to debug the code inside an used
3rd party library/module. You will have to open the source code of that library and introduce the pdb.set_trace()
code, isn't it a tedious task?
For resolving this we can use post-mortem debugging.
python -m pdb <filename>.py
Note : here pdb is run as a python module because we are using '-m' in the command.
We'll come to post-mortem debugging later, now lets practically learn how to efficiently debug the code. We'll use the above add_num function for it.
example.py
def add_num(num1,num2):
print('we have reached inside add_num function')
new_num = num1 + num2
import pdb; pdb.set_trace()
return new_num
add_num(10, 15)
<python3 example.py>
:
we have reached inside add_num function
> desktop\example.py(3)add_num()
->return new_num
(Pdb)
return
' statement, just after the line where we applied pdb.set_trace()
.
(Pdb)
in the last line of above output, which means we have entered the pdb prompt and
Python will wait for you to take further action for the debugging purpose.
desktop\example.py(3)
and '(3)' states the line number of the breakpoint in that file. And at the
end pdb shows us the function name i.e add_num()
Basic command 1 : p (print)
p
lets you print the value of a variable.
def add_num(num1,num2):
print('we have reached inside add_num function')
new_num = num1 + num2
import pdb; pdb.set_trace()
return new_num
add_num(10, 15)
we have reached inside add_num function
> desktop\example.py(3)add_num()
->return new_num
(Pdb) p new_num
(Pdb) 25.0
No more need of inserting print statements all over the code, amazing right? We can do a lot more things to investigate what is going on in current logic.
(Pdb) p new_num * 5
125
(Pdb) p new_num * 10
1250
(Pdb) p new_num + 10
35
If we again print the value of that variable we can see the value does not change after investigation.
(Pdb) p new_num
15
Basic command 2 : a (args)
To check the arguments passed in the function on which logic is applied, we can use - a (args)
:
(Pdb) a
num1 = 10
num2 = 15
Basic command 3 : whatis
It checks the data type of the variable. It is equivalent to type(argument)
in Python code.
(Pdb) whatis num1
<class 'int'>
(Pdb) whatis new_num
<class 'int'>
(Pdb) type(new_num)
<class 'int'>
Note : All other python functions can also be called through pdb prompt, like we used type()
in
the above example.
Basic command 4 : n (next)
'n'
continues the execution to the next line until the code reaches at the end of the function.--Return--
message will be displayed and in the next line the return value is
displayed.
def get_address(id):
return 'dummy-address'
def print_details(name, id):
import pdb; pdb.set_trace()
print('Name is : ', name)
address = get_address(id)
return address
print_details('getechready', 7)
> desktop\example.py(2)print_details()
->print('Name is : ', name)
(Pdb) n
Name is : getechready
> desktop\example.py(3)print_details()
->address = get_address(id)
(Pdb) n
> desktop\example.py(4)print_details()
return address
(Pdb) n
--Return--
> desktop\example.py(5)print_details()->'dummy_address'
Bonus tip : When you have to go through many lines of code while debugging, you do not have to press 'n'
everytime, after pressing 'n'
one time pdb is smart enough to understand that next action will be same only, so you can
just press "ENTER⏎"
key after it.
Basic command 5 : s (step)
--Call--
message will be displayed.'s'
also needs to be pressed once and after that "ENTER⏎"
key will do the job.
def get_address(id):
return 'dummy-address'
def print_details(name, id):
import pdb; pdb.set_trace()
print('Name is : ', name)
address = get_address(id)
return address
print_details('getechready', 7)
> desktop\example.py(2)print_details()
->print('Name is : ', name)
(Pdb) s
Name is : getechready
> desktop\example.py(3)print_details()
->address = get_address(id)
(Pdb) s
--Call--
> desktop\example.py(1)get_address()
-> def get_address()
(Pdb) s
> desktop\example.py(2)get_address()
return'dummy-address'
(Pdb) s
--Return--
> desktop\example.py(2)get_address()
return'dummy-address'
(Pdb) s
> desktop\example.py(8)get_address()
return address
Basic command 6 : ll (longlist)
ll command lists the code for current function or snippet of current frame is function is very long.
It is a very helpful command when you are dealing the large code base where you can easily get lost, just use ll
to find out where you are.
Lets run the above code once again:
> desktop\example.py(2)print_details()
->print('Name is : ', name)
(Pdb) ll
4 def print_details(name, id):
5 import pdb; pdb.set_trace()
6 -> print('Name is : ', name)
7 address = get_address(id)
8 return address
As you can notice full function is displayed and with arrow on line 6 telling us the where out debugger is.
Basic command 6 : l (list)
Use 'l'
to see the small snippet of code. This command shows 11 lines of code around the current debugging point (5 above,
5 down, and 1 current line).
We can also provide range of line numbers to see specific lines.
->print('Name is : ', name)
(Pdb) l 4,6
4 def print_details(name, id):
5 import pdb; pdb.set_trace()
6 -> print('Name is : ', name)
Basic command 7 : c (continue)
Use 'c'
when:
'c'
to skip to the next breakpoint.
def print_names(name_list):
import pdb; pdb.set_trace()
for name in name_list:
print(name)
import pdb; pdb.set_trace()
return None
print_details(['Eddy','Satya','Robin'])
> desktop\example.py(1)print_names()
-> for name in name_list:
(Pdb) c
Eddy
Satya
Robin
->return None
Note : The c
command printed all elements of name_list and again stopped the execution after for
loop
where we applied and next breakpoint.
Basic command 8 : q (quit)
Sometimes you find out while debugging that there is some issue in the code and you do not want further code to be executed.
Eg. you found out the ID passed in the function is wrong and next lines of code is saving the ID into Database, you definitely do not want
this ID to be saved in DB, at this this time you can use q
to quit the execution.
Advanced command 1 : b (break)
[([filename:]lineno | function) [, condition]]
filename and condition are optional, we'll see this in example.
Break command is very useful when:
def print_names(name_list):
for name in name_list:
print(name)
return None
print_details(['Eddy','Satya','Robin'])
Note: There is no import pdb code in the file.
python -m pdb example.py
> desktop\example.py(1)<module>()
-> def print_names(name_list):
(Pdb) b example:3
Breakpoint 1 at desktop\example.py:3
We have added breakpoint in example.py at line number 3. Now we can use c
command to continue the execution till line no.3
(Pdb) c
-> print(name)
(PDB)p name
Eddy
Example 2 (adding conditional breakpoints):
Run the command:
python -m pdb example.py
> desktop\example.py(1)<module>()
-> def print_names(name_list):
(Pdb) b example:3, name=='Robin'
Breakpoint 1 at desktop\example.py:3
(PDB) c
-> print(name)
(PDB) p name
'Robin'
Our breakpoint skipped first 2 iterations of 'Eddy' and 'Satya' and stopped execution at 'Robin' which 3rd element of the list.
Things to consider:
filename
is not provided in the command, pdb will start debugging for the current file.b (break)
can also be used with inline pdb debugging i.e. writing import pdb;pdb.set_trace() or breakpoint() inside the code.b
can be used in the pdb shell to list all the breakpoints added. b example.print_names
enable bpnumber
or disable bpnumber
command. eg. disable 3
tbreak
.
Advanced command 2 : pp (pretty printer)
pdb also includes of pretty printer.
This command is useful especially when working with big dictionaries, json or HTML code. pp
beautifies
the structure of these data structures in the terminal in a human readable manner.