Six minutes of coding and 6 hours debugging. Well, it is inevitable that most of your time will be spent on debugging rather than coding in your early stage of programming. Debugging is often annoying, frustrating, and unpredictable to know how much time it takes. In order to have a clean code while debugging would be great.
While debugging one needs to have the skill to read the code and tracebacks. Learning about debugging in the early days of programming can be really beneficial. In this tutorial, I am going to talk about how to debug your code the smarter way in python 3.8 and above and you can save a huge amount of time.
Introduction to debugging
Debugging is one of the most important parts of software development. It is the process of finding incorrect operation, calculation of steps called bugs, and the process of analysing these bugs is called debugging.
There can be multiple reasons for the failure of your code and debugging can be really painful at first. With experience in coding, and using the correct tools and modules, we can find our bug quickly and solve it. It is a really important skill that every developer should have.
In the early days of my own programming, I was using print statements to debug my code. At a later stage, I was introduced to the pdb standard library of Python. There is no need to install extra packages. Although for a smaller program we can use just a print statement, but as your code gets bigger with different modules and different classes, this is not so simple.
Some of the drawbacks of using print() include:
- For large code with lots of modules, functions, and classes, adding the print() statement is quite painful
- The code is not interactive and hence will execute automatically without any chance to investigate specific lines of code
- Once all the debugging is complete, we still have to delete those print statements
The other strategy you can take is to use logging (see our guide on logging in python 3). There are in fact ways you can use to log to files and the console to make things easier as well. This becomes useful to find bugs but also to see what happened in production – however, there is one shortcoming of logging which is that you cannot interact with the code. This is where breakpoints() can help.
How to use the python breakpoint()
Starting in Python 3.7, another efficient and flexible built-in function was introduced called breakpoint() which was easy to remember. In earlier versions before 3.7 , you can achieve a similar outcome by importing a pdb module and calling the pdb.set_trace().
It was very useful in the early phase as by inserting only the
import pdb;pdb.set_trace() we are able to pause our execution and python will bring up the interactive debugger. You can then enter a command and see all the code executed and access variables.
Let’s start the scenario without using breakpoint() in the simple example given above. We have created the function divide which will take two parameters numerator, denominator and calculate the division operation. It will work if we simply put correct arguments that can be divisible but if we put 0 on the denominator as above, we get the following exception:
As you can see the tracebacks and exceptions, we have ZeroDivisionError which is very obvious since the code is fairly simple. But imagine if we have thousands of lines of code with many variables, well this is the point where our breakpoint() comes in handy.
By simply adding the breakpoint() above the line where an error has been raised, we can see what is being processed till now. By checking the variable value, we can get to know what is actually causing the error by doing operation in pdb console.
Now let’s inject breakpoint and execute the function.
As we saw the error was happening on return operation so we put the breakpoint just above it.
By simply running the script we can see the pdb shell. You can also change the value of variables during runtime to test your hypotheses to know what is the reason behind the failure of the code.
After correcting the variable of denominator we can use the command word c which will use to continue and execute the remaining process. Another handy command is q to quit from the console. Another common set of commands include n which will execute the next line (but step over any functions). And finally, the s command will step into a function
After checking what is coming in the numerator and denominator and using the value we found while dividing by zero, we get an error. This was possible only breaking up our code flow and pausing the code in the required field using breakpoint().
Now to solve this issue we can change the code in a different way. My approach would be printing the message that we can’t perform the process if the denominator is zero and execute the process without error.
Now using knowing what was causing error we can write the code bug-free.
So simply adding the breakpoint in the required line and executing the function. All commands can be found using h on console
Some of the useful commands that we may use frequently while debugging are :
- c (continue) will continue to execute the code after breakpoint till the point where another breakpoint is defined
- n will execute the next line of code
- s will step into the function if present and will execute the function,if not just as as
- p can be used to print to test the variable value and class,q will quit the debugger/execution,
- h if you need help with debugger use ‘h’(help), which lists all the options
- args list out the arguments in the current function
One thing to be careful is not to use any of the command line names as variable names in your code. I spent quite some time trying to see why my variable “args” does not have the expected value only to realise that args shows me the current arguments.
Tips and Tricks With Python Debuggin
Here are some tips that will certainly save you a bunch of time when using the debugger:
- Add the “breakpoint()” function inside an IF statement to only fire when the condition you’re trying to debug comes up. It’ll help you find issues faster
- Use dir( object ) in order to get a list of all the available elements and functions that a given object has
- Use object.__dict__ to convert the current object into a dictionary to list our all element names and values. You can get a glimpse of an object very quickly
- Run your flask server in the foreground and use breakpoint() in your flask apps so that when you perform a web function, your breakpoints will fire
The breakpoint is a powerful weapon to debug Pythonic code which adds “effectiveness” as there’s no mess of print() statements in your code and makes your clean much cleaner and effective.
Get Notified Automatically Of New Articles
Want to see more useful articles? Subscribe to our newsletter!