I'm trying to add a __set_name__
hook to the descriptor produced by functools.wraps
inside a decorator, but it is never called and I don't see any error messages:
import functools
def wrap(fn):
"""Decorator."""
@functools.wraps(fn)
def w(*args, **kwargs):
return fn(*args, **kwargs)
# This never gets called.
def set_name(self, obj, name):
print(f"inside __set_name__: {self=}, {obj=}, {name=}")
w.__set_name__ = set_name.__get__(w)
return w
class Foo:
@wrap
def foo(self):
pass
From what I understand, wrap()
is called and its return value bound to the foo
variable in the class's execution frame before the Foo
class is created, so the __set_name__
hook should be in place by the time Python looks for it. So why isn't it being called?
CodePudding user response:
Whenever Python looks for magic methods, it looks on the type of the object, not the instance. What you've done is take a function
object (the return value of functools.wrap
, in this case) and assign something on its __dict__
. But for efficiency (and correctness, in some cases), special methods like __set_name__
bypass __dict__
and look on the type object directly. See Special method lookup for details and a rationale.
To make your code work, you need to create a custom callable class (i.e. a class which defines a function called __call__
), define __set_name__
on that class, and then make w
an instance of that class.