This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author rhettinger
Recipients rhettinger
Date 2021-11-12.05:01:10
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1636693271.06.0.414985323738.issue45791@roundup.psfhosted.org>
In-reply-to
Content
Per PEP 3119, "Overloading works as follows: The call isinstance(x, C) first checks whether C.__instancecheck__ exists, and if so, calls C.__instancecheck__(x) instead of its normal implementation."

However, this doesn't work because the isinstance() has a bug introduced in Python 3.1 and no one ever noticed.

In abstract.c::object_recursive_isinstance(), we have:

    PyObject *checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);

However, that function expects an instance as an argument rather than a class.

Calling typeobject.c::_PyObject_LookupSpecial() runs:

    res = _PyType_LookupId(Py_TYPE(self), attrid);

Note, the Py_TYPE(self) is intended to move up from an instance to a class, but we're already started with a class, so it moves to the metaclass instead.

This code was correct when originally implemented but it did not have tests:

    if (name == NULL) {
        name = PyString_InternFromString("__instancecheck__");
        if (name == NULL)
            return -1;
    }
    checker = PyObject_GetAttr(cls, name);
    if (checker == NULL && PyErr_Occurred())
        PyErr_Clear();


------- Demonstration code ---------------

class C:
    def __instancecheck__(self, inst):
        raise RuntimeError(f'{self=}  {inst=}')

    
class P:
    def __instancecheck__(self, inst):
        raise RuntimeError(f'{self=}  {inst=}')

    
class C(P):
    pass

>>> isinstance(C(), P)    # Incorrectly fails to invoke __instancecheck__
True

>>> isinstance(C(), P())  # Incorrectly invokes __instancecheck__
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    isinstance(C(), P())
  File "<pyshell#5>", line 3, in __instancecheck__
    raise RuntimeError(f'{self=}  {inst=}')
RuntimeError: self=<__main__.P object at 0x107586c80>  inst=<__main__.C object at 0x107587100>
History
Date User Action Args
2021-11-12 05:01:11rhettingersetrecipients: + rhettinger
2021-11-12 05:01:11rhettingersetmessageid: <1636693271.06.0.414985323738.issue45791@roundup.psfhosted.org>
2021-11-12 05:01:11rhettingerlinkissue45791 messages
2021-11-12 05:01:10rhettingercreate