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