Search code examples
pythontracestack-traceexecution

Trace specific functions in Python to capture high-level execution flow


Python offers tracing through its trace module. There are also custom solutions like this. But these approaches capture most low-level executions, inside-and-out of most/every library you use. Other than deep-dive debugging this isn't very useful.

It would be nice to have something that captures only the highest-level functions laid out in your pipeline. For example, if I had:

def funct1():
    res = funct2()
    print(res)

def funct2():
    factor = 3
    res = funct3(factor)
    return(res)

def funct3(factor):
    res = 1 + 100*factor
    return(res)

...and called:

funct1()

...it would be nice to capture:

function order:
    - funct1
    - funct2
    - funct3

I have looked at:

I am happy to manually mark the functions inside the scripts, like we do with Docstrings. Is there a way to add "hooks" to functions, then track them as they get called?


Solution

  • You can always use a decorator to track which functions are called. Here is an example that allows you to keep track of what nesting level the function is called at:

    class Tracker:
        level = 0
    
        def __init__(self, indent=2):
            self.indent = indent
    
        def __call__(self, fn):
            def wrapper(*args, **kwargs):
                print(' '*(self.indent * self.level) + '-' + fn.__name__)
                self.level += 1
                out = fn(*args, **kwargs)
                self.level -= 1
                return out
            return wrapper
    
    
    track = Tracker()
    
    @track
    def funct1():
        res = funct2()
        print(res)
    
    @track
    def funct2():
        factor = 3
        res = funct3(factor)
        return(res)
    
    @track
    def funct3(factor):
        res = 1 + 100*factor
        return(res)
    

    It uses the class variable level to keep track of how many functions have been called and simply prints out the the function name with a space indent. So calling funct1 gives:

    funct1()
    # prints:
    -funct1
      -funct2
        -funct3
    # returns:
    301
    

    Depending on how you want to save the output, you can use the logging module for the output