Arbitrary Stack Trace in Python

11 February 2011

I had a need to trace a function's call stack to identify call chain paths in some difficult-to-follow Python code that was laced with lots of magic and abstractions.

I tried stepping through ipdb, but it took forever. So, I thought to myself, why can't I just take a stack trace and I'll be able to get the call stack. It didn't help that this function was apparently called multiple times from different places so when I tried raising an unhandled exception, it only helped display the stack trace for the first call to the function, not the successive ones. I also tried throwing and catching the exception, assuming that a stack trace can share some information about its call chain. That didn't work too well because the exception masked the stack [1].

So I wrote some simple code that will do exactly that, take a stack trace and format it as if it looked like an exception traceback. Without further ado, here is, as far as I know, the only way to get an arbitrary stack trace in Python from any line of code.

import inspect
import traceback

# get the currently frames' stack
# this returns the frameobject, the filename,
# the line number of the current line, the
# function name, a list of lines of context from
# the source code, and the index of the current
# line within that list.

stack = inspect.stack()

# reverse the stack trace so the most recent is at the bottom of the stack

stack.reverse()
stack_list = []

try:
    for s in stack:
        _, filename, line_no, func_name, code_list, index_in_code_list = s
        stack_list.append(
           (filename, line_no, func_name, code_list[index_in_code_list])
        )
    print ''.join(traceback.format_list(stack_list))
finally:
    # avoid memory leak issues
    del stack

Hope this helps in your debugging adventures.

I'd love to hear if there are any other ways of doing the same thing.

EDIT:

My original assumption was right. Using a stack trace to get the call chain is one way of doing it.

Turns out Python already does this in the traceback.print_stack function.

Thanks to @teepark for the comment!

[1]I should probably revisit this later to see why it didn't work. In theory it should do what the stack trace snippet above does.

This entry was tagged as programming python