Greetings,

I am trying to emulate attribute lookup, and want to test if symbol found in __dict__ is an attribute or not in python code (i.e.: does its have a `tp_descr_get` slot?)

Reading the documentation carefully, I am supposed to test if the attribute has a `.__get__` method; however, I see no way to test for this properly (easily).

Currently the only way I know to test for this is (See code at end of this message):

any('__get__' in  m.__dict__   for m in type(v).__mro__)

Which seems terribly inefficient.

The documentation at:


Both says:

"""
For classes, the machinery is in type.__getattribute__() which transforms B.x into B.__dict__['x'].__get__(None, B). In pure Python, it looks like:
def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, '__get__'):
        return v.__get__(None, self)
    return v
"""

However, the call to `hasattr(v, '__get__')` appears to me to be incorrect.

The question is *NOT* whether 'v' has an attribute '__get__'; *BUT* whether `v` has a symbol `__get__` in any of the classes in it's method resolution order.

Looking at `type_getattro` in "Objects/typeobject.c" here:


Reads:

meta_get = Py_TYPE(meta_attribute)->tp_descr_get

So I really want to know if the `tp_descr_get` slot is set or not.

(Which is a different question than whether `v` has a `__get__` attribute).

The code below shows that:

1.  The valid value of `Point.y` is <Not_A_Descriptor>
2.  The valid value is returned by `Point.y`, `type.__getattribute__(Point, y)`, and `fixed__Type__getattribute`
3.  The invalid value of `Point.y` is `2` as returned by the [emulated] `__getattribute__` documented https://docs.python.org/3/howto/descriptor.html

So I am requesting:

1.  An efficient python way to test for `tp_descr_get` slot (and other descriptor) slots.
2.  Fix the documentation referenced above.

Thanks,

Joy Diamond.

NOTE #1:  This email describes the very subtle difference between whether an instance has a `__get__` symbol or not, which I believe is *NOT* the same question as whether `hasattr(instance, "__get__")` returns true or not.  The first question is does it have the symbol `__get_` [Which python put in the `tp_descr_slot`] while `hasattr` answers the question does it have the `__get__` attribute.

NOTE #2: Also using `hasattr(type(v), "__get__")` would not answer the question I want, because then it might find a `__get__` method in the meta-class of `type(v)` which again would return an incorrect answer.

Example program that shows that using `hasattr(v, '__get__')` is not a valid way to test if something is a descriptor (works in python 2, python 3, and pypy):

def get_1(self, a, b):
    return 1

def get_2(a, b):
    return 2

class Descriptor(object):
    __get__ = get_1

class Not_A_Descriptor(object):
    def __init__(self):
        self.__get__ = get_2

    def __repr__(self):
        return '<Not_A_Descriptor instance>'

class Point(object):
    __slots__ = (())

    x = Descriptor()
    y = Not_A_Descriptor()


#
#   Copied from https://docs.python.org/3/howto/descriptor.html
#
def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, '__get__'):
        return v.__get__(None, self)
    return v


#
#   My fixed version
#
def fixed__Type__getattribute(self, key):
    "FIXED: Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)

    if any('__get__' in  m.__dict__   for m in type(v).__mro__):
        return v.__get__(None, self)

    return v

print('Point.x: %s' % Point.x)
print('Point.y: %s' % Point.y)

print("type.__getattribute__(Point, 'x'): %s" % type.__getattribute__(Point, 'x'))
print("type.__getattribute__(Point, 'y'): %s" % type.__getattribute__(Point, 'y'))

print("__getattribute__(Point, 'x'): %s" % __getattribute__(Point, 'x'))
print("__getattribute__(Point, 'y'): %s  ***WRONG***" % __getattribute__(Point, 'y'))

print("fixed__Type__getattribute(Point, 'x'): %s" % fixed__Type__getattribute(Point, 'x'))
print("fixed__Type__getattribute(Point, 'y'): %s  ***CORRECT***" % fixed__Type__getattribute(Point, 'y'))