Search code examples
pythonpython-2.7decoratorpython-decorators

Getting function name from nested decorator


I am trying to get correct function name while using 2 decorators.

1> profile - from memory_profiler import profile

2> custom timing decorator

def timing(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        start = time()
        result = f(*args, **kwargs)
        end = time()
        print 'Elapsed time: {} - {}'.format(wrapper.__name__, end - start)
        return result

    return wrapper 

They are used in the following order as defined below

@timing
@profile
def my_function():
    something.....

Problem is both functions work well individually but when used together I don't get correct name via the timing decorator. I always get wrapper instead of the actual function name.

How do I get the actual function name instead of getting "wrapper" as the function name?


Solution

  • Dealt with the same problem today. In my case, I have a few decorators that extend other decorators by composition, and also take input arguments themselves. I needed to log some contextual information about the function being decorated, so I was naturally inclined to use the func.__name__ attribute, and I was getting the "wrapper" string like you.

    My solution might not be the most elegant one (by far) but it's legal and it worked 🤷

    def log_context(logger):
      def decorator(func):
        def wrapper(*args, **kwargs):
          logger.debug({
            func.__name__: {
              "args": args,
              "kwargs": kwargs,
            },
          })
    
        # Persist the name of the original function
        # throughout the stack
        wrapper.__name__ = func.__name__
        return wrapper
    
      return decorator
    

    Then I can have something like this without a problem:

    @log_context(logger)
    @a_similar_decorator(logger)
    def foo(x, y=None):
      pass
    
    foo(1, y=123)
    

    This would output something like this:

    {'foo': {'args': (1,), 'kwargs': {'y': 123}}}