When Python encounters an error in your code, it will print out a traceback.
Let's talk about
how to use tracebacks to fix our code
.
import sys
def count_to(number):
for n in range(1, number+1):
print(n)
def main():
stop = sys.argv[1]
count_to(stop)
if __name__ == "__main__":
main()
This program is supposed to accept a number and print out all the numbers up to and including that number.
But this code is broken.
When we run it, it prints out a traceback:
$ python3 count.py 5
Traceback (most recent call last):
File "/home/trey/count.py", line 12, in <module>
main()
File "/home/trey/count.py", line 9, in main
count_to(stop)
File "/home/trey/count.py", line 4, in count_to
for n in range(1, number+1):
TypeError: can only concatenate str (not "int") to str
When Python encounters an exception that isn't handled in your code, it will print out a traceback.
Read the last line in a traceback first
Tracebacks are supposed to be read from the bottom upward: the very last line in a traceback is the first line that you're supposed to read.
That last line in the traceback describes the type of exception that occurred, and it shows us an error message that's supposed to explain what happened:
TypeError: can only concatenate str (not "int") to str
The error message shown in tracebacks can sometimes be a little bit cryptic, but it's typically better than nothing.
After that last line, we're supposed to read the two lines above that:
File "/home/trey/count.py", line 4, in count_to
for n in range(1, number+1):
And then the two lines above that:
File "/home/trey/count.py", line 9, in main
count_to(stop)
And so on.
That's why the first line in a traceback says most recent call last: the most recent call in our code is the last line in the traceback).
We read tracebacks from the bottom upward.
The two lines above that last one describe where we actually were in our code when the exception occurred:
File "/home/trey/count.py", line 4, in count_to
for n in range(1, number+1):
We were in count.py
, on line 4
, in the count_to
function (note that you can see the actual line of code in that file as well).
The lines in a traceback represent frames in the "call stack"
Python uses something called a call stack to keep track of where you are in your Python process at any one time.
It keeps track of where you're supposed to go next when you return from your current function, where that function will return to, and so on.
The lines in our traceback describe our call stack.
That is, they describe where Python actually was in our process when the exception occurred:
File "/home/trey/count.py", line 12, in <module>
main()
File "/home/trey/count.py", line 9, in main
count_to(stop)
File "/home/trey/count.py", line 4, in count_to
for n in range(1, number+1):
Each of those sets of two lines are called stack frames.
You can think of Stack frames as levels of depth in our code: the bottom-most two lines in our traceback represents the deepest level, while the top-most lines represent the furthest level out from where that exception occurred.
So in our count.py
program, line 4
is where the exception actually occurred:
File "/home/trey/count.py", line 4, in count_to
for n in range(1, number+1):
Because tracebacks represent lines in the call stack, they're also sometimes called a stack trace.
In general, the deepest stack frame in our traceback won't always be our code (that is code we wrote).
It could be code from some other module besides our own.
So sometimes you'll need to read other frames in your call stack above that bottom one.
Reading up the call stack to fix the bug
Now, I happen to know that what this TypeError
means:
TypeError: can only concatenate str (not "int") to str
That error message means Python tried to use the +
operator between a string and an integer, and that's not allowed.
See the article TypeError: can only concatenate str (not "int") to str for more details.
The exception occurred on line 4, in number+1
:
import sys
def count_to(number):
for n in range(1, number+1):
print(n)
The number
variable must be pointing to a string (since 1
is an integer).
We probably shouldn't convert number
to an integer in this function, because this function isn't supposed to accept strings.
Let's go up to the next frame in our call stack!
The next frame in our call stack here is on line 9
:
File "/home/trey/count.py", line 9, in main
count_to(stop)
File "/home/trey/count.py", line 4, in count_to
for n in range(1, number+1):
This function is where we could fix this error:
def main():
stop = sys.argv[1]
count_to(stop)
The line just above count_to(stop)
, line 8, is where we should convert that string to a number.
The command line argument (sys.argv[1]
) is coming in as a string, so we should convert it to a number (by passing it to the int
function) before pointing the stop
variable to it:
def main():
stop = int(sys.argv[1])
count_to(stop)
This should fix the bug in our code.
Let's try and run our count.py
program again:
$ python3 count.py 5
It works!
We've just fixed the bug.
Read tracebacks from the bottom upward
Tracebacks happen all the time.
Reading tracebacks is just part of the process of writing Python code.
Whenever Python encounters an unhandled exception in your code, it'll print out a traceback.
Remember, tracebacks should be read from the bottom upward.
The last line tells you the exception that occurred and a (hopefully helpful) error message.
That last line is what you might want to type into your favorite search engine to figure out what might be going on.
Then the two lines above the last one tell you what line the exception actually occurred on.
Then from there, each set of two lines describes the next level of depth in our code (a.k.a. stack frames).
That's why the first line of a traceback says Traceback (most recent call last)
: "most recent call last" means the most recent level in your call stack is shown last, meaning at the very bottom.
Remember: read your tracebacks from the bottom upward.
Learn The 5 Keys to Python Success 🔑
Sign up for my free 5 day email course and learn essential concepts that introductory courses often overlook:
iterables, callables, pointers, duck typing, and namespaces.
Series: Exceptions
Exceptions happens! When exceptions happen, how should interpret the traceback for an exception? And how, when, and where should you catch exceptions?
To track your progress on this Python Morsels topic trail, sign in or sign up.
Sign in to your Python Morsels account to track your progress.
Don't have an account yet? Sign up here.
Sign up for my free 5 day email course and learn essential concepts that introductory courses often overlook:
iterables, callables, pointers, duck typing, and namespaces.
Learn to avoid beginner pitfalls, in less than a week!
Ready to level up? Sign up now to begin your Python journey the right way!