Sentry Answers>Python>

Print stack traces in Python

Print stack traces in Python

David Y.

The Problem

When a Python script encounters an unhandled exception, it will normally print a stack trace to help with debugging. This is useful, you may need more control over when and how stack traces are printed. For example, you may want to be able to:

  1. Print stack traces manually, at different points in the script’s execution, without raising an exception.
  2. Print the stack trace of a provided exception object.

The Solution

We can do both of these things using Python’s built-in traceback module, which provides stack traces in a manner identical to the Python interpreter itself.

In addition to printing stack traces, several other methods for tracing function execution in Python are available, as discussed in this answer.

The function traceback.print_stack will print a stack trace at the current point in our script’s invocation. The optional limit parameter allows us to control how many entries are printed — by default, the entire stack trace will be printed. Consider the following example script:

Click to Copy
import traceback def trace(): traceback.print_stack() def do_something(): a = 1 + 2 trace() do_something()

When executed, this script will print the following stack trace:

Click to Copy
File "/tmp/trace.py", line 9, in <module> do_something() File "/tmp/trace.py", line 7, in do_something trace() File "/tmp/trace.py", line 3, in trace traceback.print_stack()

We can use traceback.print_exception to print the traceback of an exception object we pass to it, along with the usual exception information. This function has three required arguments: the exception’s class, the exception object, and the exception’s traceback. In this scenario, we can get all of those from the same object, so it makes sense to create a wrapper function, like the one below:

Click to Copy
import traceback def print_exc(exception): traceback.print_exception(type(exception), exception, exception.__traceback__)

We can then use this wrapper function in a larger script:

Click to Copy
import traceback def print_exc(exception): traceback.print_exception(type(exception), exception, exception.__traceback__) my_dict = {} saved_exception = None try: a = my_dict["invalid"] except Exception as ex: saved_exception = ex print("An error occurred") if saved_exception: print("Here are the details of an exception that happened earlier:") print_exc(saved_exception)

When executed, this script will produce the following output:

Click to Copy
An error occurred Here are the details of an exception that happened earlier: Traceback (most recent call last): File "/tmp/manual_exc.py", line 10, in <module> a = my_dict["invalid"] ~~~~~~~^^^^^^^^^^^ KeyError: 'invalid'

Note that traceback.print_exception prints the name and details of the exception below the stack trace. If we just want the stack trace on its own, we will need to take a more manual approach, using Python’s built-in inspect module to iterate through each frame of the exception’s traceback object. For example:

Click to Copy
import inspect def print_stacktrace(exception): stacktrace = exception.__traceback__ for frame in inspect.getinnerframes(stacktrace): filename = frame.filename lineno = frame.lineno function = frame.function code_context = frame.code_context code_context = code_context[0].strip() if code_context else "No code context" print(f"File \"{filename}\", line {lineno}, in {function}") print(f" {code_context}")

The inspect.getinnerframes function returns a list of FrameInfo objects, each of which contains the details of a single frame in the stack trace, including the filename, line number, function, and code context. Inside the for loop, we retrieve each of these and then print them, using a format that mimics Python’s default stack trace printouts. Unlike traceback.print_exception, this approach gives us complete control over how the stack trace is printed.

Here’s a modified version of our example script that only prints the stack trace of the exception provided:

Click to Copy
import inspect def print_stacktrace(exception): stacktrace = exception.__traceback__ for frame in inspect.getinnerframes(stacktrace): filename = frame.filename lineno = frame.lineno function = frame.function code_context = frame.code_context code_context = code_context[0].strip() if code_context else "No code context" print(f"File \"{filename}\", line {lineno}, in {function}") print(f" {code_context}") my_dict = {} saved_exception = None try: a = my_dict["invalid"] except Exception as ex: saved_exception = ex print("An error occurred") if saved_exception: print("Here is the stack trace for the exception that happened earlier:") print_stacktrace(saved_exception)

When executed, it will print the following output:

Click to Copy
An error occured Here is the stack trace for the exception that happened earlier: File "/tmp/pst.py", line 19, in <module> a = my_dict["invalid"]
  • Sentry BlogPython Performance Testing: A Comprehensive Guide
  • Syntax.fmListen to the Syntax Podcast
  • Sentry BlogLogging in Python: A Developer’s Guide
  • CodecovPython - Codecov
  • Syntax.fm logo
    Listen to the Syntax Podcast

    Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.

    SEE EPISODES

Considered “not bad” by 4 million developers and more than 100,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.

© 2024 • Sentry is a registered Trademark of Functional Software, Inc.