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

blog comments powered by Disqus