A decorator @wrapper is using the wrapt library to access the wrapped function's class to get the class's name. Using it on Animal.method() and foo() works as expected.
Problem: However, the method Animal.classy decorated with @classmethod is giving type as its class name, and the method Animal.static decorated with @staticmethod is unable to retrieve its class.
Is it possible for @wrapper decorator function to obtain the class name Animal for Animal.classy() and Animal.static()?
Expected Output
foo
Animal.method
Animal.classy
Animal.static
Obtained Output
foo
Animal.method
type.classy
static
Code to reproduce
import wrapt
import time
@wrapt.decorator
def wrapper(wrapped, instance, args, kw):
if instance is not None:
print(f'\n{instance.__class__.__name__}.{wrapped.__name__}')
else:
print('\n' + wrapped.__name__)
result = wrapped(*args, **kw)
return result
@wrapper
def foo():
time.sleep(0.1)
class Animal:
@wrapper
def method(self):
time.sleep(0.1)
@wrapper
@classmethod
def classy(cls):
time.sleep(0.1)
@wrapper
@staticmethod
def static():
time.sleep(0.1)
if __name__ == '__main__':
foo() # foo
Animal().method() # Animal.method
Animal.classy() # type.classy
Animal.static() # static
The issue you're having is that the arg
instanceis the value for the first parameter of any method. So for a regular method this will be it'sselfvalue and for a class method it'll be theclsvalue. For a static method you have no first/instance parameter so it behaves as if it were a function andinstanceisNone.So let's walk through your code. You call
foo, your wrapper checks ifinstanceis set, it is not since functions don't get an instance parameter, so it prints out the name of the function.Next you call
methodon an instance ofAnimal. Your wrapper checks ifinstanceis set, which it is since methods get an instance parameter, so it prints out the name of the class forinstanceand the name of the method.Next you call
classyon the classAnimal. Your wrapper checks ifinstanceis set, which it is since class methods get an instance parameter, so it prints out the name of the class forinstanceand the name of the class method. However in this caseinstanceis the class that the method is defined on so when you doinstance.__class__it retrieves the metaclass for yourAnimalclass which istype. So what you actual want in this case is to add a checkif isinstance(instance, type)and in that case you want to doprint(f"{instance.__name__}.{wrapped.__name__}")since instance is your class in this case.Finally you call the static method
staticon your classAnimal. When you declare a method static it behaves as a normal function. So your wrapper checks ifinstanceis set and finds that it is not, since functions don't get instance parameters, so moves on and just prints the name of the function. There's no standard way I know of to detect a static method.So in summary to answer your exact questions. Is it possible to get the class name for
classy? Yes. And is it possible to get the class name forstatic? Not that I'm aware of.For further information on implementing a decorator that can be applied in different contexts, see:
In particular it provides the example:
That may help in understanding what steps would need to be taken in each case to calculate the name.
Hope that makes sense.