Changing diapers reminded Guido that he wanted to allow for some measure
of multiple inheritance from a mix of new- and classic-style classes. This is his patch, plus a start at some test cases from me. Will check in more, plus a NEWS blurb, later tonight.
This commit is contained in:
parent
e1d4ad3a31
commit
a91e9646e0
|
@ -829,6 +829,53 @@ def multi():
|
||||||
vereq(Frag().__int__(), 42)
|
vereq(Frag().__int__(), 42)
|
||||||
vereq(int(Frag()), 42)
|
vereq(int(Frag()), 42)
|
||||||
|
|
||||||
|
# MI mixing classic and new-style classes.
|
||||||
|
class C:
|
||||||
|
def cmethod(self):
|
||||||
|
return "C a"
|
||||||
|
def all_method(self):
|
||||||
|
return "C b"
|
||||||
|
|
||||||
|
class M1(C, object):
|
||||||
|
def m1method(self):
|
||||||
|
return "M1 a"
|
||||||
|
def all_method(self):
|
||||||
|
return "M1 b"
|
||||||
|
|
||||||
|
vereq(M1.__mro__, (M1, C, object))
|
||||||
|
m = M1()
|
||||||
|
vereq(m.cmethod(), "C a")
|
||||||
|
vereq(m.m1method(), "M1 a")
|
||||||
|
vereq(m.all_method(), "M1 b")
|
||||||
|
|
||||||
|
class D(C):
|
||||||
|
def dmethod(self):
|
||||||
|
return "D a"
|
||||||
|
def all_method(self):
|
||||||
|
return "D b"
|
||||||
|
|
||||||
|
class M2(object, D):
|
||||||
|
def m2method(self):
|
||||||
|
return "M2 a"
|
||||||
|
def all_method(self):
|
||||||
|
return "M2 b"
|
||||||
|
|
||||||
|
vereq(M2.__mro__, (M2, object, D, C))
|
||||||
|
m = M2()
|
||||||
|
vereq(m.cmethod(), "C a")
|
||||||
|
vereq(m.dmethod(), "D a")
|
||||||
|
vereq(m.m2method(), "M2 a")
|
||||||
|
vereq(m.all_method(), "M2 b")
|
||||||
|
|
||||||
|
class M3(M1, object, M2):
|
||||||
|
def m3method(self):
|
||||||
|
return "M3 a"
|
||||||
|
def all_method(self):
|
||||||
|
return "M3 b"
|
||||||
|
# XXX Expected this (the commented-out result):
|
||||||
|
# vereq(M3.__mro__, (M3, M1, M2, object, D, C))
|
||||||
|
vereq(M3.__mro__, (M3, M1, M2, D, C, object)) # XXX ?
|
||||||
|
|
||||||
def diamond():
|
def diamond():
|
||||||
if verbose: print "Testing multiple inheritance special cases..."
|
if verbose: print "Testing multiple inheritance special cases..."
|
||||||
class A(object):
|
class A(object):
|
||||||
|
@ -1016,14 +1063,6 @@ def errors():
|
||||||
class Classic:
|
class Classic:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
|
||||||
class C(object, Classic):
|
|
||||||
pass
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
verify(0, "inheritance from object and Classic should be illegal")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
class C(type(len)):
|
class C(type(len)):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -577,6 +577,47 @@ serious_order_disagreements(PyObject *left, PyObject *right)
|
||||||
return 0; /* XXX later -- for now, we cheat: "don't do that" */
|
return 0; /* XXX later -- for now, we cheat: "don't do that" */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
fill_classic_mro(PyObject *mro, PyObject *cls)
|
||||||
|
{
|
||||||
|
PyObject *bases, *base;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
assert(PyList_Check(mro));
|
||||||
|
assert(PyClass_Check(cls));
|
||||||
|
i = PySequence_Contains(mro, cls);
|
||||||
|
if (i < 0)
|
||||||
|
return -1;
|
||||||
|
if (!i) {
|
||||||
|
if (PyList_Append(mro, cls) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bases = ((PyClassObject *)cls)->cl_bases;
|
||||||
|
assert(bases && PyTuple_Check(bases));
|
||||||
|
n = PyTuple_GET_SIZE(bases);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
base = PyTuple_GET_ITEM(bases, i);
|
||||||
|
if (fill_classic_mro(mro, base) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
classic_mro(PyObject *cls)
|
||||||
|
{
|
||||||
|
PyObject *mro;
|
||||||
|
|
||||||
|
assert(PyClass_Check(cls));
|
||||||
|
mro = PyList_New(0);
|
||||||
|
if (mro != NULL) {
|
||||||
|
if (fill_classic_mro(mro, cls) == 0)
|
||||||
|
return mro;
|
||||||
|
Py_DECREF(mro);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
mro_implementation(PyTypeObject *type)
|
mro_implementation(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
|
@ -589,9 +630,13 @@ mro_implementation(PyTypeObject *type)
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
PyTypeObject *base =
|
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||||
(PyTypeObject *) PyTuple_GET_ITEM(bases, i);
|
PyObject *parentMRO;
|
||||||
PyObject *parentMRO = PySequence_List(base->tp_mro);
|
if (PyType_Check(base))
|
||||||
|
parentMRO = PySequence_List(
|
||||||
|
((PyTypeObject*)base)->tp_mro);
|
||||||
|
else
|
||||||
|
parentMRO = classic_mro(base);
|
||||||
if (parentMRO == NULL) {
|
if (parentMRO == NULL) {
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -651,26 +696,34 @@ best_base(PyObject *bases)
|
||||||
{
|
{
|
||||||
int i, n;
|
int i, n;
|
||||||
PyTypeObject *base, *winner, *candidate, *base_i;
|
PyTypeObject *base, *winner, *candidate, *base_i;
|
||||||
|
PyObject *base_proto;
|
||||||
|
|
||||||
assert(PyTuple_Check(bases));
|
assert(PyTuple_Check(bases));
|
||||||
n = PyTuple_GET_SIZE(bases);
|
n = PyTuple_GET_SIZE(bases);
|
||||||
assert(n > 0);
|
assert(n > 0);
|
||||||
base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0);
|
base = NULL;
|
||||||
winner = &PyBaseObject_Type;
|
winner = NULL;
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
base_i = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
|
base_proto = PyTuple_GET_ITEM(bases, i);
|
||||||
if (!PyType_Check((PyObject *)base_i)) {
|
if (PyClass_Check(base_proto))
|
||||||
|
continue;
|
||||||
|
if (!PyType_Check(base_proto)) {
|
||||||
PyErr_SetString(
|
PyErr_SetString(
|
||||||
PyExc_TypeError,
|
PyExc_TypeError,
|
||||||
"bases must be types");
|
"bases must be types");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
base_i = (PyTypeObject *)base_proto;
|
||||||
if (base_i->tp_dict == NULL) {
|
if (base_i->tp_dict == NULL) {
|
||||||
if (PyType_Ready(base_i) < 0)
|
if (PyType_Ready(base_i) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
candidate = solid_base(base_i);
|
candidate = solid_base(base_i);
|
||||||
if (PyType_IsSubtype(winner, candidate))
|
if (winner == NULL) {
|
||||||
|
winner = candidate;
|
||||||
|
base = base_i;
|
||||||
|
}
|
||||||
|
else if (PyType_IsSubtype(winner, candidate))
|
||||||
;
|
;
|
||||||
else if (PyType_IsSubtype(candidate, winner)) {
|
else if (PyType_IsSubtype(candidate, winner)) {
|
||||||
winner = candidate;
|
winner = candidate;
|
||||||
|
@ -827,6 +880,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
|
||||||
for (i = 0; i < nbases; i++) {
|
for (i = 0; i < nbases; i++) {
|
||||||
tmp = PyTuple_GET_ITEM(bases, i);
|
tmp = PyTuple_GET_ITEM(bases, i);
|
||||||
tmptype = tmp->ob_type;
|
tmptype = tmp->ob_type;
|
||||||
|
if (tmptype == &PyClass_Type)
|
||||||
|
continue; /* Special case classic classes */
|
||||||
if (PyType_IsSubtype(winner, tmptype))
|
if (PyType_IsSubtype(winner, tmptype))
|
||||||
continue;
|
continue;
|
||||||
if (PyType_IsSubtype(tmptype, winner)) {
|
if (PyType_IsSubtype(tmptype, winner)) {
|
||||||
|
@ -1079,16 +1134,20 @@ PyObject *
|
||||||
_PyType_Lookup(PyTypeObject *type, PyObject *name)
|
_PyType_Lookup(PyTypeObject *type, PyObject *name)
|
||||||
{
|
{
|
||||||
int i, n;
|
int i, n;
|
||||||
PyObject *mro, *res, *dict;
|
PyObject *mro, *res, *base, *dict;
|
||||||
|
|
||||||
/* Look in tp_dict of types in MRO */
|
/* Look in tp_dict of types in MRO */
|
||||||
mro = type->tp_mro;
|
mro = type->tp_mro;
|
||||||
assert(PyTuple_Check(mro));
|
assert(PyTuple_Check(mro));
|
||||||
n = PyTuple_GET_SIZE(mro);
|
n = PyTuple_GET_SIZE(mro);
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
type = (PyTypeObject *) PyTuple_GET_ITEM(mro, i);
|
base = PyTuple_GET_ITEM(mro, i);
|
||||||
assert(PyType_Check(type));
|
if (PyClass_Check(base))
|
||||||
dict = type->tp_dict;
|
dict = ((PyClassObject *)base)->cl_dict;
|
||||||
|
else {
|
||||||
|
assert(PyType_Check(base));
|
||||||
|
dict = ((PyTypeObject *)base)->tp_dict;
|
||||||
|
}
|
||||||
assert(dict && PyDict_Check(dict));
|
assert(dict && PyDict_Check(dict));
|
||||||
res = PyDict_GetItem(dict, name);
|
res = PyDict_GetItem(dict, name);
|
||||||
if (res != NULL)
|
if (res != NULL)
|
||||||
|
@ -1920,9 +1979,9 @@ PyType_Ready(PyTypeObject *type)
|
||||||
assert(PyTuple_Check(bases));
|
assert(PyTuple_Check(bases));
|
||||||
n = PyTuple_GET_SIZE(bases);
|
n = PyTuple_GET_SIZE(bases);
|
||||||
for (i = 1; i < n; i++) {
|
for (i = 1; i < n; i++) {
|
||||||
base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
|
PyObject *b = PyTuple_GET_ITEM(bases, i);
|
||||||
assert(PyType_Check(base));
|
if (PyType_Check(b))
|
||||||
inherit_slots(type, base);
|
inherit_slots(type, (PyTypeObject *)b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Some more special stuff */
|
/* Some more special stuff */
|
||||||
|
@ -1940,8 +1999,9 @@ PyType_Ready(PyTypeObject *type)
|
||||||
bases = type->tp_bases;
|
bases = type->tp_bases;
|
||||||
n = PyTuple_GET_SIZE(bases);
|
n = PyTuple_GET_SIZE(bases);
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
base = (PyTypeObject *) PyTuple_GET_ITEM(bases, i);
|
PyObject *b = PyTuple_GET_ITEM(bases, i);
|
||||||
if (add_subclass((PyTypeObject *)base, type) < 0)
|
if (PyType_Check(b) &&
|
||||||
|
add_subclass((PyTypeObject *)b, type) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3665,7 +3725,6 @@ fixup_slot_dispatchers(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
slotdef *p;
|
slotdef *p;
|
||||||
PyObject *mro, *descr;
|
PyObject *mro, *descr;
|
||||||
PyTypeObject *base;
|
|
||||||
PyWrapperDescrObject *d;
|
PyWrapperDescrObject *d;
|
||||||
int i, n, offset;
|
int i, n, offset;
|
||||||
void **ptr;
|
void **ptr;
|
||||||
|
@ -3690,13 +3749,18 @@ fixup_slot_dispatchers(PyTypeObject *type)
|
||||||
do {
|
do {
|
||||||
descr = NULL;
|
descr = NULL;
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
base = (PyTypeObject *)
|
PyObject *b = PyTuple_GET_ITEM(mro, i);
|
||||||
PyTuple_GET_ITEM(mro, i);
|
PyObject *dict = NULL;
|
||||||
assert(PyType_Check(base));
|
if (PyType_Check(b))
|
||||||
descr = PyDict_GetItem(
|
dict = ((PyTypeObject *)b)->tp_dict;
|
||||||
base->tp_dict, p->name_strobj);
|
else if (PyClass_Check(b))
|
||||||
if (descr != NULL)
|
dict = ((PyClassObject *)b)->cl_dict;
|
||||||
break;
|
if (dict != NULL) {
|
||||||
|
descr = PyDict_GetItem(
|
||||||
|
dict, p->name_strobj);
|
||||||
|
if (descr != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (descr == NULL)
|
if (descr == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
@ -3825,7 +3889,7 @@ super_getattro(PyObject *self, PyObject *name)
|
||||||
superobject *su = (superobject *)self;
|
superobject *su = (superobject *)self;
|
||||||
|
|
||||||
if (su->obj != NULL) {
|
if (su->obj != NULL) {
|
||||||
PyObject *mro, *res, *tmp;
|
PyObject *mro, *res, *tmp, *dict;
|
||||||
descrgetfunc f;
|
descrgetfunc f;
|
||||||
int i, n;
|
int i, n;
|
||||||
|
|
||||||
|
@ -3858,9 +3922,13 @@ super_getattro(PyObject *self, PyObject *name)
|
||||||
res = NULL;
|
res = NULL;
|
||||||
for (; i < n; i++) {
|
for (; i < n; i++) {
|
||||||
tmp = PyTuple_GET_ITEM(mro, i);
|
tmp = PyTuple_GET_ITEM(mro, i);
|
||||||
assert(PyType_Check(tmp));
|
if (PyType_Check(tmp))
|
||||||
res = PyDict_GetItem(
|
dict = ((PyTypeObject *)tmp)->tp_dict;
|
||||||
((PyTypeObject *)tmp)->tp_dict, name);
|
else if (PyClass_Check(tmp))
|
||||||
|
dict = ((PyClassObject *)tmp)->cl_dict;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
res = PyDict_GetItem(dict, name);
|
||||||
if (res != NULL) {
|
if (res != NULL) {
|
||||||
Py_INCREF(res);
|
Py_INCREF(res);
|
||||||
f = res->ob_type->tp_descr_get;
|
f = res->ob_type->tp_descr_get;
|
||||||
|
|
Loading…
Reference in New Issue