-1

I have the below decorator function for printing response times. Is there a way to pass description of the decorator when decorating another function with this decorator? For eg: I want to print "calling service xyz" instead of function name "call_service_xyz"

 def timer(func):
        """Print the runtime of the decorated function"""
        @functools.wraps(func)
        def wrapper_timer(*args, **kwargs):
            start_time = time.perf_counter()    # 1
            value = func(*args, **kwargs)
            end_time = time.perf_counter()      # 2
            run_time = end_time - start_time    # 3
            logger.info(f"Finished {func.__name__!r} in {run_time:.4f} secs")
            return value
        return wrapper_timer

@timer("call service xyz")
def call_service_xyz():
Punter Vicky
  • 15,954
  • 56
  • 188
  • 315
  • 1
    Where is this description supposed to come from, and what part of your code is going to use it? It's not clear what you're trying to do. – user2357112 May 28 '19 at 19:30
  • ‘Description of the decorator’? Do you mean description of the function _being decorated_? If so, that’s just `func.__doc__`. – N Chauhan May 28 '19 at 19:30
  • Thanks @NChauhan. Is there a way to pass it like this @timer("calling service xyz") – Punter Vicky May 28 '19 at 19:31
  • No, not in that way. All you need to do is write a docstring for your function and then change `func.__name__` to `func.__doc__` – N Chauhan May 28 '19 at 19:35
  • 1
    Possible duplicate of [Decorators with parameters?](https://stackoverflow.com/questions/5929107/decorators-with-parameters). Or maybe [this one](https://stackoverflow.com/q/10176226/2823755). – wwii May 28 '19 at 19:36

1 Answers1

1

You could print the docstring to act as the description of the function:

 def timer(func):
    """Print the runtime of the decorated function"""
    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()    # 1
        value = func(*args, **kwargs)
        end_time = time.perf_counter()      # 2
        run_time = end_time - start_time    # 3
        logger.info(f"{desc} Finished in {run_time:.4f} secs")
        return value
    return wrapper_timer

@timer
def call_service_xyz():
    """Call service xyz."""
    # ^^ this is the docstring which is
    # assigned to .__doc__

Alternatively, if you wish to pass the description into timer you need to create another layer to the decorator which accepts arguments:

def timer(desc):
    def decorator(func):
        @functools.wraps(func)
        def wrapper_timer(*args, **kw):
            start_time = time.perf_counter()
            value = func(*args, **kw)
            end_time = time.perf_counter()
            run_time = end_time - start_time
            logger.info(f"{func.__doc__!r} Finished in {run_time:.4f} secs")
            return value
        return wrapper_timer
    return decorator

Now use as you describe in your question:

@timer('call service xyz')
def xyz():
    ...
N Chauhan
  • 3,407
  • 2
  • 7
  • 21