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
.