I want to get list off all decorators used for function, samething which works like this
@a
@b(arg=1)
f():
pass
get_decorators(f) == ['a', 'b']
# or
get_decorators(f) == [a, b]
I want to get list off all decorators used for function, samething which works like this
@a
@b(arg=1)
f():
pass
get_decorators(f) == ['a', 'b']
# or
get_decorators(f) == [a, b]
Bring a carrot because we're going down a rabbit hole on this one...
This can be simple if the decorators are simple:
def a(func):
try:
func.decorators.append('a')
except AttributeError:
func.decorators = ['a']
# decorator business
return func
def b(arg):
def _deco(func)
try:
func.decorators.append('b')
except AttributeError:
func.decorators = ['b']
# decorator business
return func
return _deco
@a
@b(arg=1)
def my_func():
pass
print(my_func.decorators) # Go full python and put attributes on your functions
But what can you do when you have common functionality applied to multiple functions? You refactor to use decorators of course! So, may I present, the meta-decorator. (I also upped the ante by changing b
to return a wrapper)
def deco_tracker(decorator):
""" Meta-decorator for attaching decorator reference to decorated function
Applying this decorator to another decorator will result in the final decorated function
having an attribute called 'decorators' that stores references to it's decorators.
"""
def deco_wrapper(func):
returned_func = decorator(func) # Sometimes a wrapper is returned
try:
returned_func.decorators.append(decorator)
except AttributeError:
returned_func.decorators = getattr(func, 'decorators', []) + [decorator]
return returned_func
return deco_wrapper
@deco_tracker
def a(func):
# decorator business
return func
def b(arg):
@deco_tracker
def _deco(func)
def wrapper(*args, **kwargs)
# decorator business
return func(*args, **kwargs)
return wrapper
return _deco
@a
@b(arg=1)
def my_func():
pass
print(my_func.decorators) # [<function b.<locals>._deco 0x19>, <function a at 0x7f>]
It was easier to store the function reference instead of a string representation of the name. It negates the complexity of parsing a wrapper.
Meta-decorator can be applied to third party decorators as long as they don't return a wrapper. For that you would need to wrap deco_tracker
to produce deco_wrapper_tracker
then apply the correct meta-decorator variant to each decorator. An enhancement to that would be to check if the first incoming argument isinstance
of types.FunctionType
and select from there but that assumes decorator that takes args never take a function, pretty safe bet but not ideal. If this gets enough attention I'll produce the example code.