I'm trying to create a wrapper for methods.
The decorator itself is implemented as a class that overwrites the __call__ method.
This works for decorating functions, but if a method is decorated this way, self is not bound to the decorated function.
The following is a minimal example:
import functools
class Wrapper:
def __init__(self, func) -> None:
super().__init__()
self.__func = func
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
print("PreBar")
result = self.__func(*args, **kwargs)
print("PostBar")
return result
def MyDecorator(func):
return Wrapper(func)
class Foo:
baz = "Baz"
@MyDecorator
def bar(self):
print(self.baz)
return self.baz
if __name__ == '__main__':
foo = Foo()
print(foo.bar)
print(f"Result of foo.bar(): {foo.bar()}")
print(Foo.bar)
print(f"Result of Foo.bar(foo): {Foo.bar(foo)}")
This produces the following error, meaning that self is not automatically passed to the __call__ invocation as with normal methods:
Traceback (most recent call last):
File "/mypath/test.py", line 35, in <module>
print(f"Result of foo.bar(): {foo.bar()}")
File "/mypath/test.py", line 14, in __call__
result = self.__func(*args, **kwargs)
TypeError: bar() missing 1 required positional argument: 'self'
Question: How can the returned callable wrapper object be bound to self?
Wrapping the object in a function to let python do the binding is an option, but not in my case, since the actual object is needed.
The question How can I decorate an instance method with a decorator class? is similar to what i want to achieve. The solution provided there works if only the binding is required. What i require though, is that the object instance that is returned when accessing the attribute is of type Wrapper, not of type functools.partial.
The second answer to that question also does not work, since the instance returned by __get__ is of type method and not Wrapper.