Message406187
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> |
|
Date |
User |
Action |
Args |
2021-11-12 05:01:11 | rhettinger | set | recipients:
+ rhettinger |
2021-11-12 05:01:11 | rhettinger | set | messageid: <1636693271.06.0.414985323738.issue45791@roundup.psfhosted.org> |
2021-11-12 05:01:11 | rhettinger | link | issue45791 messages |
2021-11-12 05:01:10 | rhettinger | create | |
|