Commit 586da8fd authored by Michael W. Hudson's avatar Michael W. Hudson
Browse files

Readjustments to the way we cope with exceptions from subclasses'

mro() methods.  Now any exception aborts the whole __bases__ change.

And more tests.
parent 31ddfb69
......@@ -3434,7 +3434,7 @@ def do_this_first():
# (before PyType_Ready(tuple) is called)
type.mro(tuple)
def mutable_bases():
def test_mutable_bases():
# stuff that should work:
class C(object):
pass
......@@ -3523,6 +3523,80 @@ def mutable_bases():
else:
raise TestFailed, "new-style class must have a new-style base"
def test_mutable_bases_with_failing_mro():
class WorkOnce(type):
def __new__(self, name, bases, ns):
self.flag = 0
return super(WorkOnce, self).__new__(WorkOnce, name, bases, ns)
def mro(self):
if self.flag > 0:
raise RuntimeError, "bozo"
else:
self.flag += 1
return type.mro(self)
class WorkAlways(type):
def mro(self):
# this is here to make sure that .mro()s aren't called
# with an exception set (which was possible at one point).
# An error message will be printed in a debug build.
# What's a good way to test for this?
return type.mro(self)
class C(object):
pass
class C2(object):
pass
class D(C):
pass
class E(D):
pass
class F(D):
__metaclass__ = WorkOnce
class G(D):
__metaclass__ = WorkAlways
# Immediate subclasses have their mro's adjusted in alphabetical
# order, so E's will get adjusted before adjusting F's fails. We
# check here that E's gets restored.
E_mro_before = E.__mro__
try:
D.__bases__ = (C2,)
except RuntimeError:
vereq(E.__mro__, E_mro_before)
else:
raise TestFailed, "exception not propagated"
def test_mutable_bases_catch_mro_conflict():
class A(object):
pass
class B(object):
pass
class C(A, B):
pass
class D(A, B):
pass
class E(C, D):
pass
try:
C.__bases__ = (B, A)
except TypeError:
pass
else:
raise TestFailed, "didn't catch MRO conflict"
def mutable_names():
class C(object):
pass
......@@ -3608,8 +3682,11 @@ def test_main():
slotmultipleinheritance()
testrmul()
testipow()
mutable_bases()
test_mutable_bases()
test_mutable_bases_with_failing_mro()
test_mutable_bases_catch_mro_conflict()
mutable_names()
if verbose: print "All OK"
if __name__ == "__main__":
......
......@@ -142,11 +142,11 @@ static void remove_subclass(PyTypeObject *, PyTypeObject *);
static void update_all_slots(PyTypeObject *);
static int
mro_subclasses(PyTypeObject *type)
mro_subclasses(PyTypeObject *type, PyObject* temp)
{
PyTypeObject *subclass;
PyObject *ref, *subclasses, *old_mro;
int i, n, r = 0;
int i, n;
subclasses = type->tp_subclasses;
if (subclasses == NULL)
......@@ -164,22 +164,27 @@ mro_subclasses(PyTypeObject *type)
old_mro = subclass->tp_mro;
if (mro_internal(subclass) < 0) {
subclass->tp_mro = old_mro;
r = -1;
return -1;
}
else {
Py_DECREF(old_mro);
PyObject* tuple;
tuple = Py_BuildValue("OO", subclass, old_mro);
if (!tuple)
return -1;
if (PyList_Append(temp, tuple) < 0)
return -1;
}
if (mro_subclasses(subclass) < 0)
r = -1;
if (mro_subclasses(subclass, temp) < 0)
return -1;
}
return r;
return 0;
}
static int
type_set_bases(PyTypeObject *type, PyObject *value, void *context)
{
int i, r = 0;
PyObject* ob;
PyObject *ob, *temp;
PyTypeObject *new_base, *old_base;
PyObject *old_bases, *old_mro;
......@@ -247,8 +252,25 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context)
return -1;
}
if (mro_subclasses(type) < 0)
r = -1;
temp = PyList_New(0);
r = mro_subclasses(type, temp);
if (r < 0) {
for (i = 0; i < PyList_Size(temp); i++) {
PyTypeObject* cls;
PyObject* mro;
PyArg_ParseTuple(PyList_GetItem(temp, i),
"OO", &cls, &mro);
Py_DECREF(cls->tp_mro);
cls->tp_mro = mro;
Py_INCREF(cls->tp_mro);
}
Py_DECREF(temp);
return r;
}
Py_DECREF(temp);
/* any base that was in __bases__ but now isn't, we
need to remove |type| from it's tp_subclasses.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment