At work, my friend Jon and I were looking into timing the various decorators in the app (bitbucket.org). We didn’t want to manually modify each decorator we were hoping to time. I ended up exploring the possibility of decorating decorators. It was an approach that could allow us to make the minimum modification to a decorator (just slapping on the timing decorator) and with very little duplicate code.

Here’s the basic approach:

import time
from functools import wraps

def timed(decorator):
    def wrapped_decorator(func):
        recorded_time = []
        def timed(func):
            @wraps(func)
            def timed_func(*args, **kwargs):
                start = time.time()
                rv = func(*args, **kwargs)
                if recorded_time:
                    outer_time = time.time() - start
                    print 'Decorator time: ', outer_time - recorded_time.pop()
                else:
                    recorded_time.append(time.time() - start)
                return rv
            return timed_func
        return timed(decorator(timed(func)))
    return wrapped_decorator
    
@timed
def upper(func):
    def wrapped():
        time.sleep(1.3)
        return func().upper()
    return wrapped
    
@upper
def hello():
    time.sleep(0.7)
    return 'hello'
    
if __name__ == '__main__':
    print hello()