Being able to debug is a core skill for every developer. Do you use a print() statement regularly, or do you prefer to use debugging tools?
Let’s find out how you can improve your debugging experience by understanding the Python stack trace and by using it to fix bugs.
Why Is Debugging Important, and What Does It Cost Organizations?
According to recent Cambridge University research, the global cost of debugging software has risen to $312 billion annually—and debugging takes up half the development time of the average project.
This number is expected to grow as more products and services are digitized. Yet there’s a lack of interest in software debugging—even in the domain of debugging tools and approaches that would make developers’ lives easier.
Even today, many developers rely on the power of the print() command. Are you one of them?
What Is the Impact of Inadequate Debugging?
If you take the easy way out by using a print() statement, then you run the risk of spending 50% of your time debugging code. That makes you partly responsible for higher project costs and delays. However, by regularly adding logging statements to your code, you can make it easier to debug.
Now that computer programs run most electronics, there’s no room for mistakes. Imagine you’re in a car accident. Because of a software bug, your airbag doesn’t go off. In this case, a simple bug can end your life. Also, companies are becoming more aware of the reputation damage that service outages cause—even if those outages are brief.
So, are you debugging the right way? Let’s explore how to use the Python stack trace to your advantage for debugging.
What Is a Python Stack Trace?
Python prints a stack trace when your code throws an exception. The stack trace might look overwhelming when you see it for the first time. However, the Python stack trace holds a lot of valuable information that helps you identify the source of the problem. Understanding what information a Python stack trace provides is vital to becoming a better Python programmer.
Example of a Python Stack Trace
A stack trace report contains the function calls made in your code right before the error occurred. When your program raises an exception, it will print the stack trace. Below is an example of a simple Python script that will raise an exception.
# example.py def say(name): print('Hello, ' + nam) say('Michael')
Here, the say() function accepts a parameter name. However, we made a mistake by using the wrong variable inside the print() statement. As you can see, we reference nam instead of name.
When you run the program with python example.py, it should return this stack trace:
As you can see, this stack trace contains a lot of information about what’s gone wrong. First of all, it tells you what type of error has occurred: NameError. This type of exception tells us that we’ve referenced a variable that doesn’t exist. As you read further, it will tell you what variable we tried to reference. Here, nam is not defined. In general, this exception tells you that a variable, function, or class has been referenced incorrectly.
How Can You Read a Python Stack Trace?
Read a stack trace from bottom to top. Why? The last line of a stack trace contains the most recent call. It’s easier to start where things went wrong and work your way up through the code.
Elements of a Python Stack Trace
Let’s learn about the elements that make up a stack trace:
- Blue underline: The last line of a traceback tells you which exception your code has raised.
- Green underline: After the exception, the stack trace prints the error message. The error message contains a more detailed reason your code has thrown this exception. This message helps you analyze the issue and figure out what’s gone wrong.
- Orange boxes: The text in the orange boxes indicates the file, line number, and module name or function specifying where the problematic code is. As you can see, a stack trace may contain many of these that lead you to the actual error.
- Red boxes: The red box shows the actual code that’s been executed. As you might have noticed, a stack trace contains multiple calls that all consist of two lines in the stack trace. This stack trace contains two calls: say() and print(). In this particular stack trace, the error happened inside the print() function.
What Are the Most Common Python Stack Traces?
Knowing how to interpret a Python stack trace is one thing. However, knowing what some of the most common Python stack traces means can speed up your debugging process.
Here are some of the most common Python stack traces you might come across and what they mean.
Your code will throw an AttributeError when you try to access an attribute on an object that doesn’t have this attribute defined. Here’s what the Python Standard Library has to say about this error:
Raised when an attribute reference or assignment fails.
Let’s see how we can reproduce this error:
# example2.py my_num = 1 my_num.param
The following snippet of code will generate this AttributeError.
As you can see, the error message tries to tell us that there’s no attribute “param” on the defined object. The declaration actually contains an integer. It’s a common error, as you’d expect a different type for the object you’re working with.
Your Python script will throw an ImportError when you mess up the import statement. The most common way is by importing a nonexistent module. This is how the Python documentation defines an ImportError exception.
Raised when the import statement has troubles trying to load a module. Also raised when the “from list” in from … import has a name that cannot be found.
Let’s try to reproduce the ImportError:
# example3.py import xyz
The following snippet of code results in the stack trace shown here.
Let’s see how we can get a SyntaxError. According to the official Python documentation, a SyntaxError is raised when:
The parser encounters a syntax error.
A syntax error can be as simple as a missing colon after the function definition:
# example4.py def say(name) print('Hello, ' + name) say('Michael')
The following stack trace printed when you execute the file. As you can see, the ^ (caret) symbol points to where the problem happened. Here, it indicates that some syntax is missing at the end of the function declaration. Unfortunately, it doesn’t tell us exactly what’s wrong with the syntax.
Other common errors include:
- TypeError: “Raised when an operation or function is applied to an object of inappropriate type.”
- ValueError: “Raised when an operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.”
- KeyError: “Raised when a mapping (dictionary) key is not found in the set of existing keys.”
- IndexError: “Raised when a sequence subscript is out of range.”
What Did You Learn About the Python Stack Trace?
The Python stack trace is a valuable piece of information that you can use to debug your code. It contains information about the call stack and points out where things have gone wrong. At the end of a stack trace, you can always find the exact exception type and a detailed message of what’s gone wrong.
If you’re still using print() statements, I hope this article helped you understand why debugging matters and how you can use the Python stack trace to solve bugs.
If you want to learn more about log formatting, check this Scalyr article that talks about some best practices.
This post was written by Michiel Mulders. Michiel is a passionate blockchain developer who loves writing technical content. Besides that, he loves learning about marketing, UX psychology, and entrepreneurship. When he’s not writing, he’s probably enjoying a Belgian beer!