My solution after taking a lot of inspiration from the answer of @canni.
And from @aurzenligl to deal with static and classmethods
.
Edit: I also added support for decorating properties
I have this class that can be reused for creating multiple decorators:
class MethodCheck:
def __init__(self, func) -> None:
self.func = func
def __get__(self, instance, owner):
if isinstance(self.func, staticmethod):
return lambda *args, **kwargs: self.on_staticmethod(self.func.__func__, *args, **kwargs)
elif isinstance(self.func, classmethod):
return lambda *args, **kwargs: self.on_classmethod(self.func.__func__, owner, *args, **kwargs)
elif isinstance(self.func, property):
return self.on_property(self.func, instance)
return lambda *args, **kwargs: self.on_method(self.func, instance, *args, **kwargs)
def __call__(self, *args, **kwargs):
return self.on_function(self.func, *args, **kwargs)
def on_method(self, func, instance, *args, **kwargs):
return func(instance, *args, **kwargs)
def on_property(self, func:property, instance, *args, **kwargs):
return func.fget(instance, *args, **kwargs)
def on_classmethod(self, func, cls, *args, **kwargs):
return func(cls, *args, **kwargs)
def on_staticmethod(self, func, *args, **kwargs):
return func(*args, **kwargs)
def on_function(self, func, *args, **kwargs):
return func(*args, **kwargs)
The implementation of a decorator using the above class:
from functools import wraps
class MethodCheckTester(MethodCheck):
def on_method(self, *args, **kwargs):
print(f"Executing Method: ", end='')
return super().on_method(*args, **kwargs)
def on_property(self, *args, **kwargs):
print(f"Accessing Property: ", end='')
return super().on_property(*args, **kwargs)
def on_classmethod(self, *args, **kwargs):
print(f"Executing Class Method: ", end='')
return super().on_classmethod(*args, **kwargs)
def on_staticmethod(self, *args, **kwargs):
print(f"Executing Static Method: ", end='')
return super().on_staticmethod(*args, **kwargs)
def on_function(self, *args, **kwargs):
print(f"Executing Function: ", end='')
return super().on_function(*args, **kwargs)
def decorator(func):
return wraps(func)(MethodCheckTester(func))
Decorated test functions/methods:
class MyClass:
@decorator
def f1(self, arg1):
print(arg1)
@decorator
@classmethod
def f2(cls, arg1):
print(arg1)
@decorator
@staticmethod
def f3(arg1):
print(arg1)
@decorator
@property
def f5(self):
return "f5"
@decorator
def f4(arg1):
print(arg1)
obj = MyClass()
obj.f1("f1")
obj.f2("f2")
obj.f3("f3")
f4("f4")
print(obj.f5)
This will print:
Executing Method: f1
Executing Class Method: f2
Executing Static Method: f3
Executing Function: f4
Accessing Property: f5