Reputation: 774
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?
Upvotes: 9
Views: 2877
Reputation: 5156
As others have pointed out, you do not seem to use decorator
correctly.
Alternately you can use my library makefun
to create your signature-preserving wrapper, it relies on the same trick than decorator
to preserve signature, but is more focused on dynamic function creation and is more generic (you can change the signatures):
from makefun import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper._decorator_name_ = 'my_decorator'
return wrapper
You can check that it works as expected:
@my_decorator
def my_func(x):
"""my function"""
print('hello %s' % x)
assert my_func._decorator_name_ == 'my_decorator'
help(my_func)
For what it's worth, if you wish to later add optional arguments to your decorator without making the code look more complex, have a look at decopatch
. For example if you want _decorator_name_
to be an optional argument of the decorator:
from decopatch import function_decorator, DECORATED
from makefun import wraps
@function_decorator
def my_decorator(name='my_decorator', func=DECORATED):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper._decorator_name_ = name
return wrapper
Upvotes: 0
Reputation: 32716
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_)
Upvotes: 12
Reputation: 22314
You only need to define a wrapper
function if you somehow want to alterate the behaviour of your function. So in the case you really just want to add some attribute to your function without changing its behaviour, you can simply do the following.
def my_decorator(func):
func._decorator_name_ = 'my_decorator'
return func
In the more complex case where you need to have a wrapper
, what I suggest is following the accepted answer for this question which explains how to create a function that behaves the same as another, but has a customised signature. Using inspect.getargspec
you can recover the signature from my_func
and transpose it to your wrapper
.
Upvotes: 3
Reputation: 146510
@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'
Upvotes: 6