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:
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:
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:
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:
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:
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:
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:
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:
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:
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"]
Get actionable, code-level insights to resolve Python performance bottlenecks and errors.
Create a free Sentry account
Create a Python project and note your DSN
Grab the Sentry Python SDK
pip install --upgrade sentry-sdk
import sentry_sdk sentry_sdk.init( "https://<key>@sentry.io/<project>", # Set traces_sample_rate to 1.0 to capture 100% # of transactions for performance monitoring. # We recommend adjusting this value in production. traces_sample_rate=1.0, )
Loved by over 4 million developers and more than 90,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.