Is it possible to conduct "advanced" time monitoring in Python using a decorator?

I have a simple set of functions that use each other. For instance:

def func(x)
    y = func_1(x)
    z = func_2(y)
    return z

def func_1(x):
    return x + 1

def func_2(x)
    a = func_a(x)
    b = func_b(y)
    return b

As you can see, functhis is the “root” function, which uses func_1and func_2and func_2, in turn, uses func_aand func_b. When I call func, I get zas a result.

Now I would like to "change" or "expand" my functions with the help of a decorator, so at the end (as a result func) I get not only z, but also an object that shows me how much this function had to be performed, and also what functions were used by the function and how much time it took to complete these “subfunctions,” as well as what “subfunctions” were used by what “subfunctions" and how long it took to execute them. To simplify this, I will give an example of what I expect as an "additional" result:

{
    'fname' : 'func',
    'etime' : 12.000,
    'subs'  : [
        {
        'fname' : 'func_1',
        'etime' : 2.000,
        'subs'  : []
        },
        {
        'fname' : 'func_2',
        'etime' : 10,
        'subs'  : [
            {
            'fname' : 'func_a',
            'etime' : 6,
            'subs' : []
            },
            {
            'fname' : 'func_b',
            'etime' : 4
            'subs' : []
            }
        ]
        }
    ]
}

"fname" , "etime" ( ), "subs" - , . ( "fname", "etime", "subs" ). , "" . - , "subs" .

:

def decorate(func):

    def wrapper(*args, **kw):

        d = {}
        d['fname'] = func.__name__
        t0 = time.time()

        out = func(*args, **kw)

        d['etime'] = time.time() - t0

        d['subs'] = ?

        ?.append(d)

    return wrapper

. , .

, , . , , , .

+4
1

, .

, . , .

class profile(object):
    #class variable used as a stack of subs list
    stack = [[]] 

    def __init__(self, f):
        self.f = f

    def __call__(self, *args, **kw):
        func = dict(fname = self.f.__name__)

        #append the current function in the latest pushed subs list
        profile.stack[-1].append(func)

        #push a new subs list in the stack
        profile.stack.append([]) 

        #execution time of the actual call
        t0 = time.time()
        out = self.f(*args, **kw)
        func['etime'] = time.time() - t0

        #pull the subs list from the stack
        func['subs'] = profile.stack.pop()

        return out

    @classmethod
    def show(cls):
        import json #useful to prettify the ouput
        for func in cls.stack[0]:
            print json.dumps(func, sort_keys=True, indent=4)

, , @profile. , , .

Ouput:

profile.show() "" , .

{
    "etime": 4.5, 
    "fname": "func", 
    "subs": [
        {
            "etime": 1.0, 
            "fname": "func_1", 
            "subs": []
        }, 
        {
            "etime": 3.5, 
            "fname": "func_2", 
            "subs": [
                {
                    "etime": 1.5, 
                    "fname": "func_a", 
                    "subs": []
                }, 
                {
                    "etime": 2.0, 
                    "fname": "func_b", 
                    "subs": []
                }
            ]
        }
    ]
}
+3

All Articles