Reputation: 1851
I realize much ink has already been spilled on decorators, but I can't find another question which exactly covers this. I have a class, and I would like to 'tag' some of the methods in that class using a decorator. In theory this is straightforward since decorators run when the class is defined. However, I also need to record some user-entered parameters from this decorator. So something like this:
@classmethod
def record_func(cls, is_function_special):
def wrapped(func):
return func
cls.recorded_functions.append((func, is_function_special))
return wrapped
class TestClass:
recorded_functions = []
@record_func(False)
def func1():
return 1
@get_funcs(True)
def func2():
return 2
The idea being that I would then be able to use this TestClass.recorded_functions
elsewhere.
The problem (maybe one of many) is that it is only the outer-level decorator (in this case, record_func rather than wrapped) that runs when the class is defined, which has the argument in its namespace but not the function. Is there some clever way to record both the argument and the function?
NB, I've made the decorator a classmethod in this example so that it's generalizable to other classes.
Any help appreciated.
Upvotes: 0
Views: 210
Reputation: 575
I like this problem, I was working on a decorator package and I think this could be a great addition. However, let me know if this gets you going in the right direction. It can record every method call, record its args, and kwargs. Then you can do any analytics/aggregation with those results you need.
import functools
def bvr_start(arg=None):
def bvr_start_decorator(func):
@functools.wraps(func)
def bvr_start_wrapper(*args, **kwargs):
msg = ("STARTED | "
"FUNCTION: {} | "
"ARGS: {} | "
"KWARGS: {} ").format(func.__name__,
args,
kwargs)
args[0].called_functions.append(msg)
print(msg)
return_value = func(*args, **kwargs)
return return_value
return bvr_start_wrapper
if callable(arg):
return bvr_start_decorator(arg)
return bvr_start_decorator
class Hello:
called_functions = []
@bvr_start
def test_function_one(self):
print("Inside my function one")
@bvr_start
def test_function_two(self, x):
print("Inside my function two")
@bvr_start
def test_function_three(self, y):
print("Inside my function three")
hello = Hello()
hello.test_function_one()
hello.test_function_two(4)
hello.test_function_three(y=5)
print("Now print out your called functions")
for called_function in hello.called_functions:
print(called_function)
Now the result of this is:
STARTED | FUNCTION: test_function_one | ARGS: (<__main__.Hello object at 0x1038ef2d0>,) | KWARGS: {}
Inside my function one
STARTED | FUNCTION: test_function_two | ARGS: (<__main__.Hello object at 0x1038ef2d0>, 4) | KWARGS: {}
Inside my function two
STARTED | FUNCTION: test_function_three | ARGS: (<__main__.Hello object at 0x1038ef2d0>,) | KWARGS: {'y': 5}
Inside my function three
Now print out your called functions
STARTED | FUNCTION: test_function_one | ARGS: (<__main__.Hello object at 0x1038ef2d0>,) | KWARGS: {}
STARTED | FUNCTION: test_function_two | ARGS: (<__main__.Hello object at 0x1038ef2d0>, 4) | KWARGS: {}
STARTED | FUNCTION: test_function_three | ARGS: (<__main__.Hello object at 0x1038ef2d0>,) | KWARGS: {'y': 5}
Now you have a record of the function and the argument. And you can do some more dict like formatting on the final list of called_functions to count how many times each was called with what!
Thanks, and let me know if this helps!
Upvotes: 1