Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python decorator to keep signature and user defined attribute

I have my simple decorator my_decorator which decorates the my_func.

def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    wrapper._decorator_name_ = 'my_decorator'
    return wrapper

@my_decorator
def my_func(x):
    print('hello %s'%x)

my_func._decorator_name_
'my_decorator'

Till here things work, but I can't see the actual signature of the function.

my_func?
Signature: my_func(*args, **kwargs)
Docstring: <no docstring>
File:      ~/<ipython-input-2-e4c91999ef66>
Type:      function

If I decorate my decorator with python's decorator.decorator, I can see the signature of my function but I can't have the new property which I have defined.

import decorator

@decorator.decorator
def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    wrapper._decorator_name_ = 'my_decorator'
    return wrapper

@my_decorator
def my_func(x):
    print('hello %s'%x)

my_func?
Signature: my_func(x)
Docstring: <no docstring>
File:      ~/<ipython-input-8-934f46134434>
Type:      function

my_func._decorator_name_
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-10-7e3ef4ebfc8b> in <module>()
----> 1 my_func._decorator_name_

AttributeError: 'function' object has no attribute '_decorator_name_'

How can I have both in python2.7?

like image 930
Jayendra Parmar Avatar asked Feb 12 '18 12:02

Jayendra Parmar


2 Answers

For Python 3, using functools.wraps in standard library:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    wrapper._decorator_name_ = 'my_decorator'
    return wrapper

@my_decorator
def my_func(x):
    print('hello %s'%x)

print(my_func._decorator_name_)
like image 168
plaes Avatar answered Sep 19 '22 21:09

plaes


Working

@decorator.decorator returns a function which takes another function as input. In your case you want the attribute on the returned function.

For it to work on Python 2.7, you just need some tweak

import decorator

def my_dec2(func):
    @decorator.decorator
    def my_decorator(func, *args, **kwargs):
        print("this was called")
        return func(*args, **kwargs)

    test = my_decorator(func)
    test._decorator_name_ = "my_decorator"
    return test

@my_dec2
def my_func(x):
    print('hello %s'%x)


my_func(2)
print(my_func._decorator_name_)

And then when you test it works

In [1]: my_func?
Signature: my_func(x)
Docstring: <no docstring>
File:      ~/Desktop/payu/projects/decotest/decos.py
Type:      function

In [2]: my_func._decorator_name_
Out[2]: 'my_decorator'
like image 20
Tarun Lalwani Avatar answered Sep 18 '22 21:09

Tarun Lalwani