Nesting descriptors/decorators in python

descriptor, python, python-2.7, python-decorators

Solution

You can make your code work if you make `MyProperty` apply the descriptor protocol to its wrapped object:

class MyProperty(object):
    def __init__(self, fget):
        self.fget = fget
    def __get__(self, obj, objtype=None):
        print('IN MyProperty.__get__')
        try:
            return self.fget.__get__(obj, objtype)()
        except AttributeError: # self.fget has no __get__ method
            return self.fget(obj)

Now your example code works:

class A(object):
    @MyProperty
    @MyClassMethod
    def klsproperty(cls):
        return 555

print(A.klsproperty)

The output is:

IN MyProperty.__get__
IN MyClassMethod.__get__
555

Problem

I'm having a hard time understanding what happens when I try to nest descriptors/decorators. I'm using python 2.7. For example, let's take the following simplified versions of `property` and `classmethod`: ``` class MyProperty(object): def __init__(self, fget): self.fget = fget def __get__(self, obj, objtype=None): print 'IN MyProperty.__get__' return self.fget(obj) class MyClassMethod(object): def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): print 'IN MyClassMethod.__get__' def f(*args, **kwargs): return self.f(objtype, *args, **kwargs) return f ``` Trying to nest them: ``` class A(object): # doesn't work: @MyProperty @MyClassMethod def klsproperty(cls): return 555 # works: @MyProperty def prop(self): return 111 # works: @MyClassMethod def klsmethod(cls, x): return x**2 % print A.klsproperty IN MyProperty.__get__ ... TypeError: 'MyClassMethod' object is not callable ``` The `__get__` method of the inner descriptor `MyClassMethod` is not getting called. Failing to figure out why, I tried throwing in (what I think is) a no-op descriptor: ``` class NoopDescriptor(object): def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): print 'IN NoopDescriptor.__get__' return self.f.__get__(obj, objtype=objtype) ``` Trying to use the no-op descriptor/decorator in nesting: ``` class B(object): # works: @NoopDescriptor @MyProperty def prop1(self): return 888 # doesn't work: @MyProperty @NoopDescriptor def prop2(self): return 999 % print B().prop1 IN NoopDescriptor.__get__ IN MyProperty.__get__ 888 % print B().prop2 IN MyProperty.__get__ ... TypeError: 'NoopDescriptor' object is not callable ``` I don't understand why `B().prop1` works and `B().prop2` does not. Questions: - What am I doing wrong? Why am I getting a `object is not callable` error? - What's the right way? e.g. what is the best way to define `MyClassProperty` while re-using `MyClassMethod` and `MyProperty` (or `classmethod` and `property`)

Original source