Efficient way to determine whether a particular function is on the stack in Python

前端 未结 2 886
轮回少年
轮回少年 2021-01-06 03:30

For debugging, it is often useful to tell if a particular function is higher up on the call stack. For example, we often only want to run debugging code when a certain funct

2条回答
  •  南笙
    南笙 (楼主)
    2021-01-06 03:46

    I don't really like this approach, but here's a fixed-up version of what you were doing:

    from collections import defaultdict
    import threading
    functions_on_stack = threading.local()
    
    def record_function_on_stack(f):
        def wrapped(*args, **kwargs):
            if not getattr(functions_on_stack, "stacks", None):
                functions_on_stack.stacks = defaultdict(int)
            functions_on_stack.stacks[wrapped] += 1
    
            try:
                result = f(*args, **kwargs)
            finally:
                functions_on_stack.stacks[wrapped] -= 1
                if functions_on_stack.stacks[wrapped] == 0:
                    del functions_on_stack.stacks[wrapped]
            return result
    
        wrapped.orig_func = f
        return wrapped
    
    def function_is_on_stack(f):
        return f in functions_on_stack.stacks
    
    def nested():
        if function_is_on_stack(test):
            print "nested"
    
    @record_function_on_stack
    def test():
        nested()
    
    test()
    

    This handles recursion, threading and exceptions.

    I don't like this approach for two reasons:

    • It doesn't work if the function is decorated further: this must be the final decorator.
    • If you're using this for debugging, it means you have to edit code in two places to use it; one to add the decorator, and one to use it. It's much more convenient to just examine the stack, so you only have to edit code in the code you're debugging.

    A better approach would be to examine the stack directly (possibly as a native extension for speed), and if possible, find a way to cache the results for the lifetime of the stack frame. (I'm not sure if that's possible without modifying the Python core, though.)

提交回复
热议问题