Click here for the sequel of this post
Most of my work at Arbisoft involves developing and debugging custom features for Open edX based systems.
Most of my colleagues here prefer to use PyCharm as their default IDE and editor. PyCharm is among the best Python development tools out there, however, I prefer to use Sublime Text 3 in my workflow so I have to compromise on most of the tooling that PyCharm provides.
One of the most important tools is the debugger, and I’ve had to find alternatives to PyCharm’s debugger. In this post, I’ll give a demonstration of a few tools we can use to debug Python code without relying on an IDE. First on our list is:
pdb – The Python Debugger
pdb
ships with every python installation and requires zero third party tooling to get working. We’ll learn how to use it with a small walk-through.
Consider the following code:
def foo(bar=[]):
bar.append("baz")
return bar
def test1():
response = foo()
response = foo()
return response
What do you think test1()
will return?
I thought that it would simply return an array with a single element i.e "baz"
['baz']
But instead it returns an array with two elements, both "baz"
['baz', 'baz']
So, let’s debug this using pdb
. pdb
comes installed with Python. You can set a breakpoint in your code using the following statement
import pdb; pdb.set_trace()
So, to debug the above code, let’s add a breakpoint using pdb in the test1()
function
def test1():
import pdb;pdb.set_trace()
response = foo()
response = foo()
return response
We can run this code and we will see that pdb will stop the script at the intended line
~/workspace/test$ python python.py
-> response = foo()
(Pdb)
Now I can interact with the code by typing commands into the shell. Some of the commands I use are
n (next)
– Continues execution until the next line in the current function is reached or it returnss (step)
– Executes the current line and stop at the first possible occasion. This is equivalent tostep into
on most GUI based debuggersc (continue)
– Continues execution and stops only when another breakpoint is encountered.p (print)
– Takes an expression and prints its outputl (list)
– Lists the source code of the current file
Let us use these commands to see the flow of our test1()
function
As you can see in the video above, I step into the second call of foo
and check the value of bar
which turns out to be ['baz']
instead of the initial value []
.
The reason for this is a little tricky: The default values passed to function arguments are evaluated only once in Python.
Meaning that the expressions []
is evaluated as an empty list and the reference of that list assigned to bar
. So, when foo()
is called again, the same reference is stored in bar
which results in 'baz'
being appended to the same list. Hence we get ['baz', 'baz']
in response
.
To correct this situation, we can modify code as under:
def foo(bar=None):
if bar is None:
bar = []
bar.append('baz')
return bar
Now on every call of foo()
a new empty list []
will be assigned to bar
making sure that our code works as expected.
Conclusion
I realize that pdb
is not an exact replace for PyCharm but in most situations it gets the job done. Also, one advantage of pdb
being a terminal based debugger is that I can use it in any environment even virtualized environments like Docker and Vagrant and remote environments through SSH.
You can read more about how to use pdb
and all features it provides in the official Python documentation here.
I hope I’ve done a decent enough explanation on how to use pdb
. If you have any questions feel free to drop a message and I’ll try to answer them.
Cheers!