The inspect.ismethoddescriptor() function reports descriptor objects which implement __delete__() but not __set__() as method descriptors (though they are, in fact, data descriptors):
>>> class D1:
... def __get__(*_): print('get')
...
>>> class D2:
... def __get__(*_): print('get')
... def __set__(*_): print('set')
...
>>> class D3:
... def __get__(*_): print('get')
... def __delete__(*_): print('delete')
...
>>> inspect.ismethoddescriptor(D1())
True
>>> inspect.ismethoddescriptor(D2())
False
>>> inspect.ismethoddescriptor(D3()) # Note this!
True
>>> class Owner:
... d1 = D1()
... d2 = D2()
... d3 = D3()
...
>>> o.__dict__['d1'] = 42
>>> o.d1
42
>>> o.__dict__['d2'] = 42
>>> o.d2
get
>>> o.d2 = ...
set
>>> o.__dict__['d3'] = 42
>>> o.d2 # Note this!
get
>>> del o.d2 # Note this!
delete
On the other hand, this function works that way for a long time (probably, since the beginning of its existence), and its docs are consistent with its behavior (mentioning only __get__() and __set__(), but not __delete__()) – though, unfortunately, not explicit about ignoring __delete__().
I’d be happy to create an issue + PR with a fix! My question is what action would be most appropriate… In particular whether I should:
-
(1) fix the code + the docs – so that the presence of
__delete__()will start to be checked by this function in Python 3.14?- (1a) …or, if we consider it a bugfix, maybe in 3.13? (thouhg it feels too intrusive for a bugfix release)
-
(2) change nothing in the code + add a warning to the docs – that the function does not work properly for descriptors with
__get__()and__delete__()that do not have__set__()? (i.e., that they are erroneously considered method descriptors, even though they are in fact data descriptors) -
(3) same as (2) + add a new function which will work correctly, i.e., will not ignore the presence of
__delete__()?
Please note that the “sibling” function inspect.isdatadescriptor() works correctly (i.e., it takes into consideration both __set__() and __delete__()):
>>> inspect.isdatadescriptor(D1())
False
>>> inspect.isdatadescriptor(D2())
True
>>> inspect.isdatadescriptor(D3())
True