I am trying to create a decorator which can be defined on the class and decorates everything defined in it. First let me show the setup that I got already based on other SO answers:
import inspect
# https://stackoverflow.com/a/18421294/577669
def log(func):
def wrapped(*args, **kwargs):
try:
print("Entering: [%s]" % func)
try:
# https://stackoverflow.com/questions/19227724/check-if-a-function-uses-classmethod
if inspect.ismethod(func) and func.__self__: # class method
return func(*args[1:], **kwargs)
if inspect.isdatadescriptor(func):
return func.fget(args[0])
return func(*args, **kwargs)
except Exception as e:
print('Exception in %s : (%s) %s' % (func, e.__class__.__name__, e))
finally:
print("Exiting: [%s]" % func)
return wrapped
class trace(object):
def __call__(self, cls): # instance, owner):
for name, m in inspect.getmembers(cls, lambda x: inspect.ismethod(x) or inspect.isfunction(x)):
setattr(cls, name, log(m))
for name, m in inspect.getmembers(cls, lambda x: inspect.isdatadescriptor(x)):
setattr(cls, name, property(log(m)))
return cls
@trace()
class Test:
def __init__(self, arg):
self.arg = arg
@staticmethod
def static_method(arg):
return f'static: {arg}'
@classmethod
def class_method(cls, arg):
return f'class: {arg}'
@property
def myprop(self):
return 'myprop'
def normal(self, arg):
return f'normal: {arg}'
if __name__ == '__main__':
test = Test(1)
print(test.arg)
print(test.static_method(2))
print(test.class_method(3))
print(test.myprop)
print(test.normal(4))
When removing the @trace
decorator from the class, this is the output:
123
static
class
myprop
normal
When adding the @trace
decorator I get this:
Entering: [<function Test.__init__ at 0x00000170FA9ED558>]
Exiting: [<function Test.__init__ at 0x00000170FA9ED558>]
1
Entering: [<function Test.static_method at 0x00000170FB308288>]
Exception in <function Test.static_method at 0x00000170FB308288> : (TypeError) static_method() takes 1 positional argument but 2 were given
Exiting: [<function Test.static_method at 0x00000170FB308288>]
None
Entering: [<bound method Test.class_method of <class '__main__.Test'>>]
Exiting: [<bound method Test.class_method of <class '__main__.Test'>>]
class: 3
Entering: [<property object at 0x00000170FB303E08>]
Exiting: [<property object at 0x00000170FB303E08>]
myprop
Entering: [<function Test.normal at 0x00000170FB308438>]
Exiting: [<function Test.normal at 0x00000170FB308438>]
normal: 4
Conclusion from this example: the init, normal, class and prop methods are all instrumented correctly.
However, the static method is not.
My questions for this snippet are:
- Is it ok to check certain usecases like I did in the log? Or is there a better way?
- How to see if something is a static method to be able to pass in nothing (because now the Test-instance is passed in)?
Thanks!