David Y.
—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"]
Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.
SEE EPISODESConsidered “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.
Here’s a quick look at how Sentry handles your personal information (PII).
×We collect PII about people browsing our website, users of the Sentry service, prospective customers, and people who otherwise interact with us.
What if my PII is included in data sent to Sentry by a Sentry customer (e.g., someone using Sentry to monitor their app)? In this case you have to contact the Sentry customer (e.g., the maker of the app). We do not control the data that is sent to us through the Sentry service for the purposes of application monitoring.
Am I included?We may disclose your PII to the following type of recipients:
You may have the following rights related to your PII:
If you have any questions or concerns about your privacy at Sentry, please email us at compliance@sentry.io.
If you are a California resident, see our Supplemental notice.