Closed
Description
If the following is implemented using static types C API, the derived class doesn't inherit __getitem__
:
class SimpleMap:
def __getitem__(self, key): return 'value'
class SimpleObject:
pass
class DerivedObject(SimpleObject, SimpleMap):
pass
C implementation
#include <Python.h>
/* SimpleMap: A simple object that allows subscription */
static PyObject *
simplemap_subscript(PyObject *self, PyObject *key)
{
return PyUnicode_FromString("value");
}
static PyMappingMethods simplemap_as_mapping = {
.mp_subscript = simplemap_subscript,
};
static PyTypeObject simplemap_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "SimpleMap",
.tp_as_mapping = &simplemap_as_mapping,
};
/* SimpleObject: A simple object that does nothing */
static PyTypeObject simpleobject_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "reproducer.SimpleObject",
};
/* DerivedObject: Derives from SimpleObject and SimpleMap */
static PyTypeObject derivedobject_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "reproducer.DerivedObject",
.tp_new = PyType_GenericNew,
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
.m_name = "reproducer",
};
PyMODINIT_FUNC PyInit_reproducer(void)
{
PyObject *m;
m = PyModule_Create(&moduledef);
if (!m) {
return NULL;
}
if (PyType_Ready(&simplemap_type) < 0) {
return NULL;
}
if (PyModule_AddType(m, &simplemap_type) < 0) {
return NULL;
}
if (PyType_Ready(&simpleobject_type) < 0) {
return NULL;
}
if (PyModule_AddType(m, &simpleobject_type) < 0) {
return NULL;
}
derivedobject_type.tp_bases = Py_BuildValue("(OO)", &simpleobject_type, &simplemap_type);
if (!derivedobject_type.tp_bases) {
return NULL;
}
if (PyType_Ready(&derivedobject_type) < 0) {
return NULL;
}
if (PyModule_AddType(m, &derivedobject_type) < 0) {
return NULL;
}
return m;
}
>>> import reproducer
>>> instance = reproducer.DerivedObject()
>>> type(instance).mro()
[<class 'reproducer.DerivedObject'>, <class 'reproducer.SimpleObject'>, <class 'SimpleMap'>, <class 'object'>]
>>> instance['key']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'reproducer.DerivedObject' object is not subscriptable
The reason is that type_ready_inherit_as_structs
is only called wih one base, not all of them.
I don't think there's a good way to fix it properly. To allow mixing slots from multiple bases (e.g. if the other base had __setitem__
), we'd need to allocate new sub-slot structs for the derived type -- as heap types do. Also, it sounds pretty scary to change such long-standing behavior :)
The best we could do that I can think of would be to detect the situation and emit warnings -- possibly DeprecationWarnings, which could be changed to errors in a few releases.