I am trying to build a control structure in a class method that takes a function as input and has different behaviors if a function is decorated or not. Any ideas on how you would go about building a function is_decorated that behaves as follows:
def dec(fun):
# do decoration
def func(data):
# do stuff
@dec
def func2(data):
# do other stuff
def is_decorated(func):
# return True if func has decorator, otherwise False
is_decorated(func) # False
is_decorated(func2) # True
Yes, it's relatively easy because functions can have arbitrary attributes added to them, so the decorator function can add one when it does its thing:
def dec(fun):
def wrapped(*args, **kwargs):
pass
wrapped.i_am_wrapped = True
return wrapped
def func(data):
... # do stuff
@dec
def func2(data):
... # do other stuff
def is_decorated(func):
return getattr(func, 'i_am_wrapped', False)
print(is_decorated(func)) # -> False
print(is_decorated(func2)) # -> True
There are several ways to identify if a function is wrapped/decorated.
TLDR Solution
def is_decorated(func):
return hasattr(func, '__wrapped__') or func.__name__ not in globals()
Scenario A
If we assume the decorator uses the helper decorator functools.wraps, then it is pretty straight forward, since our decorated function is going to have the attribute __wrapped__ after.
from functools import wraps
def decorator(function):
@wraps(function)
def _wrapper(*a, **kw): ...
return _wrapper
@decorator
def foo(x, y): ...
def is_decorated(function):
return hasattr(function, '__wrapped__')
Scenario B
In case we don't use wraps during the decorator's definition, then it's a bit different.
def decorator(function):
def _wrapper(*a, **kw): ...
return _wrapper
@decorator
def foo(x, y): ...
def bar(x, y): ...
print(bar.__name__) # prints 'bar'
print(foo.__name__) # prints '_wrapper' instead of 'foo'
def is_decorated(function):
"""
globals() returns a dictionary which includes
defined functions, classes and many other stuff.
Assuming we haven't defined the inner/wrapper name
as an outer function elsewhere, then it will not be
in globals()
"""
return function.__name__ not in globals()
Conclusion
There are other - more sophisticated - ways to check this, however readability is as important as completeness is, and this solution is as simple it can be. As mentioned in the previous code block, the only "hole" of this solution is the following situation:
from functools import wraps
def is_decorated(func):
return hasattr(func, '__wrapped__') or func.__name__ not in globals()
def decorator_wraps(function):
@wraps(function)
def _wrapper(*a, **kw): ...
return _wrapper
def decorator_no_wraps(function):
def _wrapper(*a, **kw): ...
return _wrapper
@decorator_wraps
def foo(x, y): ...
@decorator_no_wraps
def bar(x, y): ...
def baz(x, y): ...
for function in (foo, bar, baz):
print(is_decorated(function)) # True, True, False
def _wrapper(): ...
for function in (foo, bar, baz):
print(is_decorated(function)) # True, False, False
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With