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?
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.