Updated On : Jan-14,2021 Tags traceback
traceback - How to Extract, Format, and Print Error Stack Traces in Python

traceback - How to Extract, Format, and Print Error Stack Traces in Python?

The traceback of error generated by python interpreter can be sometimes long and not that useful. We might need to modify stack traces according to our need in some situations where we need more control over the amount of traceback getting printed. We might even need more control over the format of the trace getting printed. Python provides a module named traceback which has a list of method which let us extract error traces, format it and print it according to our need. This can help with formatting and limiting error trace getting generated. As a part of this tutorial, we'll be explaining how we can use a different method of traceback module for different purposes. We'll explain the usage of the method with simple examples.

There is three kinds of methods available with the traceback module.

  • print_*() - These methods are used to print stack traces to the desired output medium (standard error, standard output, file, etc).
    • print_tb()
    • print_exception()
    • print_exc()
    • print_list()
    • print_last()
    • print_stack()
  • extract_*() - These methods are used to extract stack traces from an exception and return preprocessed stack trace entries.
    • extract_tb()
    • extract_stack()
  • format_*() - These methods are used to format output generated by extract_* methods. These methods can be useful if we want to print an error stack on some GUI.
    • format_tb()
    • format_list()
    • format_exception_only()
    • format_exception()
    • format_exc()
    • format_stack()
  • walk_*() - These methods returns generator instance which lets us iterate through trace frames and print stack trace according to our need.
    • walk_stack()
    • walk_tb()

We'll now explain the usage of these methods with simple examples one by one.

Example 1

As a part of our first example, we'll explain how we can print the stack trace of error using print_tb() method.

  • print_tb(tb, limit=None, file=None) - This method accepts traceback instance and prints traces to the output. It let us limit the amount of trace getting printed by specifying limit parameter. We can give integer value to limit parameter and it'll print trace for that many frames only. If we don't specify a limit then by default it'll be None and will print the whole stack trace. It has another important parameter named file which lets us specify where we want to direct output of trace to. We can give a file-like object and it'll direct traces to it. We can also specify standard error and standard output using sys.err and sys.out as file parameters and trace will be directed to them. If we don't specify the file parameter then by default it'll direct trace to standard error.

Below we have artificially tried to generate an error by giving randint() method of random module negative integer numbers. We have the first printed traceback generated by the python interpreter. Then we have caught exceptions using try-except block. We have then retrieved traceback from the error object using traceback attribute of the error object and have given it to print_tb() method which prints a stack trace.

Please make a note that print_tb() only prints stack trace and does not print actual exception type.

In [1]:
import random

out = random.randint(-5,-10)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-1-a38bfad2efe7> in <module>
      1 import random
      2
----> 3 out = random.randint(-5,-10)

~/anaconda3/lib/python3.7/random.py in randint(self, a, b)
    220         """
    221
--> 222         return self.randrange(a, b+1)
    223
    224     def _randbelow(self, n, int=int, maxsize=1<<BPF, type=type,

~/anaconda3/lib/python3.7/random.py in randrange(self, start, stop, step, _int)
    198             return istart + self._randbelow(width)
    199         if step == 1:
--> 200             raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
    201
    202         # Non-unit step argument supplied.

ValueError: empty range for randrange() (-5,-9, -4)
In [2]:
import traceback
import random

try:
    out = random.randint(-5,-10)
except Exception as e:
    traceback.print_tb(e.__traceback__)
  File "<ipython-input-2-200195b56950>", line 5, in <module>
    out = random.randint(-5,-10)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))

Below we have again executed code same as the previous cell but this time we have set the limit parameter to 1 hence it only prints the first trace frame.

In [3]:
import traceback
import random

try:
    out = random.randint(-5,-10)
except Exception as e:
    traceback.print_tb(e.__traceback__, limit=1)
  File "<ipython-input-3-6335bba4bb4d>", line 5, in <module>
    out = random.randint(-5,-10)

Example 2

As a part of this example, we have explained how we can direct the trace generated to a file. We have directed the error trace generated by the code to a file named traceback_ex1.out.

In [4]:
import traceback
import random

try:
    out = random.randint(-5,-10)
except Exception as e:
    traceback.print_tb(e.__traceback__, file=open("traceback_ex1.out", "w"))
In [5]:
!cat traceback_ex1.out
  File "<ipython-input-4-d02890406d60>", line 5, in <module>
    out = random.randint(-5,-10)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))

Example 3

As a part of this example, we have introduced two other methods print_exception() and print_exc().

  • print_exception(exception_type, value, tb, limit=None, file=None, chain=True) - This method print exception information as well as error stack trace. We need to provide it exception type, exception value, and traceback. It'll then print exceptions and traceback. The chain parameter is a boolean flag indicating whether we should include trace for chained exceptions or not.
  • print_exc(limit=None, file=None, chain=True) - This method whenever called will print the last exception which had happened in the program.

Please make a note that parameter limit, file, and chain are present in multiple methods of traceback module and it has the same meaning in all of them.

In this example, we have called method named exc_info() of sys module that returns tuple (exception type, exception value, exception traceback). This tuple has information about a recent exception that was caught by try-except block. We have then given this tuple to print_exception() method to print an error stack trace. We have directed stack trace to standard output in all of our examples.

Please make a note that when we used print_exception() method, it printed information about exceptions as well which was not getting printed with print_tb() method.

In [6]:
import traceback
import random
import sys


try:
    out = random.randint(-5,-10)
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print("==================Traceback 1 =========================")
    traceback.print_tb(exc_traceback, file=sys.stdout)

    print("\n==================Traceback 2 ===================")
    traceback.print_exception(exc_type, exc_value, exc_traceback, file=sys.stdout)

    print("\n==================Traceback 3 ===================")
    traceback.print_exc(file=sys.stdout)
==================Traceback 1 =========================
  File "<ipython-input-6-bf6708f55f27>", line 7, in <module>
    out = random.randint(-5,-10)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))

==================Traceback 2 ===================
Traceback (most recent call last):
  File "<ipython-input-6-bf6708f55f27>", line 7, in <module>
    out = random.randint(-5,-10)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
ValueError: empty range for randrange() (-5,-9, -4)

==================Traceback 3 ===================
Traceback (most recent call last):
  File "<ipython-input-6-bf6708f55f27>", line 7, in <module>
    out = random.randint(-5,-10)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
ValueError: empty range for randrange() (-5,-9, -4)

Example 4

As a part of our fourth example, we have demonstrated how we can catch the exception and print stack trace using print_exception() method. We have not retrieved exception information using sys.exc_info() this time. We have artificially generated divide by zero error in this example.

In [7]:
import traceback
import random

try:
    out = 10/0
except Exception as e:
    traceback.print_exception(type(e), e, e.__traceback__, file=sys.stdout)
Traceback (most recent call last):
  File "<ipython-input-7-c90920d6446e>", line 5, in <module>
    out = 10/0
ZeroDivisionError: division by zero

Example 5

We'll use our fifth example to demonstrate the usage of print_last() method.

  • print_last(limit=None, file=None, chain=True) - This method prints last exception which had happened.

We have first artificially generated error and then printed the error stack by calling print_last() method.

In [8]:
out = 10/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-8-ff29a4977585> in <module>
----> 1 out = 10/0

ZeroDivisionError: division by zero
In [9]:
traceback.print_last()
Traceback (most recent call last):
  File "/home/sunny/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3418, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-8-ff29a4977585>", line 1, in <module>
    out = 10/0
ZeroDivisionError: division by zero

Example 6

As a part of our sixth example, we'll demonstrate usage of extract_tb(), format_list() and print_list() methods.

  • extract_tb(tb, limit=None) - This method accepts traceback object and returns StackSummary instance. The StackSummary instance has list of FrameSummary instance where each FrameSummary instance has information about one frame of stack trace.
  • format_list(extracted_list) - This method takes as input StackSummary instance or list of FrameSummary instances as input and returns a list of strings representing stack trace.
  • print_list(extracted_list, file=None) - This method takes as input StackSummary instance or list of FrameSummary instances as input and prints stack trace.

Below we have first extracted the error stack using extract_tb() method and then printed it in different formats. We have also explained how we can use format_list() and print_list() methods. The StackSummary instance also has format() method which works exactly like format_list() method.

In [10]:
import traceback
import random
import sys

try:
    out = random.randint(-5,-10)
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()

    traces = traceback.extract_tb(exc_traceback)

    print("Type of Traceback         : ", type(traces))
    print("Type of Individual Traces : ", type(traces[0]))

    print(" \n========== Trace Format 1 ===============\n")
    print("%50s | %10s | %5s | %10s" %("File Name", "Method Name", "Line Number", "Line"))
    print("-"*100)
    for frame_summary in traces:
        print("%50s | %11s | %11d | %10s"%(frame_summary.filename, frame_summary.name, frame_summary.lineno, frame_summary.line))
        print("-"*100)

    print(" \n========== Trace Format 2 ===============\n")
    for trace_line in traces.format():
        print(trace_line)

    print(" \n========== Trace Format 3 ===============\n")
    for trace_line in traceback.format_list(traces):
        print(trace_line)

    print(" \n========== Trace Format 4 ===============\n")
    traceback.print_list(traces, file=sys.stdout)
Type of Traceback         :  <class 'traceback.StackSummary'>
Type of Individual Traces :  <class 'traceback.FrameSummary'>

========== Trace Format 1 ===============

                                         File Name | Method Name | Line Number |       Line
----------------------------------------------------------------------------------------------------
                   <ipython-input-10-e08cb7446344> |    <module> |           6 | out = random.randint(-5,-10)
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |     randint |         222 | return self.randrange(a, b+1)
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |   randrange |         200 | raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
----------------------------------------------------------------------------------------------------

========== Trace Format 2 ===============

  File "<ipython-input-10-e08cb7446344>", line 6, in <module>
    out = random.randint(-5,-10)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))


========== Trace Format 3 ===============

  File "<ipython-input-10-e08cb7446344>", line 6, in <module>
    out = random.randint(-5,-10)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))


========== Trace Format 4 ===============

  File "<ipython-input-10-e08cb7446344>", line 6, in <module>
    out = random.randint(-5,-10)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))

Example 7

As a part of our seventh example, we'll explain usage of methods format_tb(), format_exception() and format_exc().

  • format_tb() - This method works exactly the same as print_tb() method with the only difference that it returns a list of strings where each string is a single trace of the stack.
  • format_exception() - This method works exactly the same as print_exception() method with the only difference that it returns a list of strings where each string is a single trace of the stack.
  • format_exc() - This method works exactly the same as print_exc() method with the only difference that it returns a string that has stack trace.

Below we have explained how we can use these methods with a simple example.

In [11]:
import traceback
import random
import sys


try:
    out = random.randint(-5,-10)
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print("==================Traceback 1 =========================")
    for trace in traceback.format_tb(exc_traceback):
        print(trace)

    print("\n==================Traceback 2 ===================")
    for trace in traceback.format_exception(exc_type, exc_value, exc_traceback):
        print(trace)

    print("\n==================Traceback 3 ===================")
    print(traceback.format_exc())
==================Traceback 1 =========================
  File "<ipython-input-11-cd6fba9720d8>", line 7, in <module>
    out = random.randint(-5,-10)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))


==================Traceback 2 ===================
Traceback (most recent call last):

  File "<ipython-input-11-cd6fba9720d8>", line 7, in <module>
    out = random.randint(-5,-10)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)

  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))

ValueError: empty range for randrange() (-5,-9, -4)


==================Traceback 3 ===================
Traceback (most recent call last):
  File "<ipython-input-11-cd6fba9720d8>", line 7, in <module>
    out = random.randint(-5,-10)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
ValueError: empty range for randrange() (-5,-9, -4)

Example 8

As a part of our eighth example, we'll demonstrate the usage of format_exception_only() method.

  • format_exception_only(exception_type, exception_value) - This method takes as input exception type and exception value. It then return list of string specifying exception description.

Below we have explained the usage of it with simple example.

In [12]:
import traceback
import random

try:
    out = 10/0
except Exception as e:
    for exception_line in traceback.format_exception_only(type(e), e):
        print(exception_line)
ZeroDivisionError: division by zero

Example 9

As a part of our ninth example, we'll demonstrate the usage of walk_tb() and StackSummary.extract() methods.

  • walk_tb(tb) - This method takes as input the traceback instance and returns a generator that has a list of frames of the stack trace.
  • StackSummary.extract() - This method takes as input frame generator which has stack trace and generates StackSummary from it.

The StackSummary class has another method named from_list(frame_summary_list) which takes as an input list of FrameSummary instances and generates StackSummary instance from it. It can also take a list of tuples (filename, line no, name, line) as input to generate the StackSummary instance.

Below we have explained how we can generate StackSummary instance from the frame generator generated by walk_tb() method. We have then formatted stack trace as well using the StackSummary instance.

In [13]:
import traceback
import random
import sys

try:
    out = random.randint(-5,-10)
except Exception as e:
    tbk = e.__traceback__
    print("\n==================Traceback 1 ===================\n")
    for frame in traceback.walk_tb(tbk):
            print(frame)

    print("\n==================Traceback 2 ===================\n")
    stack_summary = traceback.StackSummary.extract(traceback.walk_tb(tbk))

    print("%50s | %10s | %5s | %10s" %("File Name", "Method Name", "Line Number", "Line"))
    print("-"*100)

    for frame_summary in stack_summary:
        print("%50s | %11s | %11d | %10s"%(frame_summary.filename, frame_summary.name, frame_summary.lineno, frame_summary.line))
        print("-"*100)
==================Traceback 1 ===================

(<frame at 0x7faebc361248, file '<ipython-input-13-3a3aaaa264bb>', line 11, code <module>>, 6)
(<frame at 0x7faebc2e9768, file '/home/sunny/anaconda3/lib/python3.7/random.py', line 222, code randint>, 222)
(<frame at 0x7faebc2fb248, file '/home/sunny/anaconda3/lib/python3.7/random.py', line 200, code randrange>, 200)

==================Traceback 2 ===================

                                         File Name | Method Name | Line Number |       Line
----------------------------------------------------------------------------------------------------
                   <ipython-input-13-3a3aaaa264bb> |    <module> |           6 | out = random.randint(-5,-10)
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |     randint |         222 | return self.randrange(a, b+1)
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |   randrange |         200 | raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
----------------------------------------------------------------------------------------------------

Example 10

As a part of our tenth example, we have demonstrated the usage of print_stack() method.

  • print_stack(f=None, limit=None, file=None) - This method takes as input frame instance and prints trace back from that frame onwards.

Below we have explained how we can use print_stack() method.

In [14]:
import traceback
import random
import sys

try:
    out = random.randint(-5,-10)
except Exception as e:
    tbk = e.__traceback__

    while tbk:
        print("Line No : ", tbk.tb_lineno)
        #print("Frame : ", tbk.tb_frame)
        print("========== Trace ==========")
        traceback.print_stack(tbk.tb_frame, limit=1, file=sys.stdout)
        print()
        tbk = tbk.tb_next
Line No :  6
========== Trace ==========
  File "<ipython-input-14-240546dff3ae>", line 14, in <module>
    traceback.print_stack(tbk.tb_frame, limit=1, file=sys.stdout)

Line No :  222
========== Trace ==========
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)

Line No :  200
========== Trace ==========
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))

Example 11

As a part of our eleventh example, we have demonstrated the usage of format_stack() method.

  • format_stack(f=None, limit=None) - This method is exactly the same as print_stack() method with the only difference that it returns a list of strings instead of printing the stack trace.

Below we have explained the usage of format_stack() with a simple example that has code almost the same as print_stack() with minor changes.

In [15]:
import traceback
import random
import sys

try:
    out = random.randint(-5,-10)
except Exception as e:
    tbk = e.__traceback__

    while tbk:
        print("Line No : ", tbk.tb_lineno)
        #print("Frame : ", tbk.tb_frame)
        print("========== Trace ==========")
        print(traceback.format_stack(tbk.tb_frame, limit=1)[0])
        print()
        tbk = tbk.tb_next
Line No :  6
========== Trace ==========
  File "<ipython-input-15-f2267a4aafa9>", line 14, in <module>
    print(traceback.format_stack(tbk.tb_frame, limit=1)[0])


Line No :  222
========== Trace ==========
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 222, in randint
    return self.randrange(a, b+1)


Line No :  200
========== Trace ==========
  File "/home/sunny/anaconda3/lib/python3.7/random.py", line 200, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))


Example 12

As a part of our twelfth example, we have demonstrated the usage of extract_stack() method.

  • extract_stack(f=None, limit=None) - This method works exactly print_stack() with only difference that it returns list of FrameSummary instance rather than printing stack trace.

We have explained the usage of extract_stack() with a simple example below. We can even generate StackSummary instance from the output of extract_stack() method.

In [16]:
import traceback
import random
import sys

try:
    out = random.randint(-5,-10)
except Exception as e:
    tbk = e.__traceback__

    print("%50s | %10s | %5s | %10s" %("File Name", "Method Name", "Line Number", "Line"))
    print("-"*100)

    while tbk:
        trace = list(traceback.extract_stack(tbk.tb_frame, limit=1))[0]
        print("%50s | %11s | %11d | %10s"%(trace.filename, trace.name, trace.lineno, trace.line))
        print("-"*100)

        tbk = tbk.tb_next
                                         File Name | Method Name | Line Number |       Line
----------------------------------------------------------------------------------------------------
                   <ipython-input-16-181d69cd0b74> |    <module> |          14 | trace = list(traceback.extract_stack(tbk.tb_frame, limit=1))[0]
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |     randint |         222 | return self.randrange(a, b+1)
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |   randrange |         200 | raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
----------------------------------------------------------------------------------------------------

Example 13

As a part of our thirteenth and last example, we'll demonstrate the usage of walk_stack() method.

  • walk_stack(f) - This method works exactly like extract_stack() method but returns generator of stack trace frames. We can generate a StackSummary instance from the output of this method.

Below we have explained the usage of the method with simple example. We have first generated StackSummary instance from the output of walk_stack() method. We have then formatted the stack trace.

In [17]:
import traceback
import random
import sys

try:
    out = random.randint(-5,-10)
except Exception as e:
    tbk = e.__traceback__

    print("%50s | %10s | %5s | %10s" %("File Name", "Method Name", "Line Number", "Line"))
    print("-"*100)
    while tbk:
        stack_summary  = traceback.StackSummary.extract(traceback.walk_stack(tbk.tb_frame))

        for frame_summary in stack_summary[:1]:
            print("%50s | %11s | %11d | %10s"%(frame_summary.filename, frame_summary.name, frame_summary.lineno, frame_summary.line))
            print("-"*100)

        tbk = tbk.tb_next

                                         File Name | Method Name | Line Number |       Line
----------------------------------------------------------------------------------------------------
                   <ipython-input-17-ce4b3126d8ff> |    <module> |          13 | stack_summary  = traceback.StackSummary.extract(traceback.walk_stack(tbk.tb_frame))
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |     randint |         222 | return self.randrange(a, b+1)
----------------------------------------------------------------------------------------------------
     /home/sunny/anaconda3/lib/python3.7/random.py |   randrange |         200 | raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
----------------------------------------------------------------------------------------------------

This ends our small tutorial explaining the usage of the traceback module. Please feel free to let us know your views in the comments section.



Sunny Solanki  Sunny Solanki