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

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(): ...@@ -3434,7 +3434,7 @@ def do_this_first():
# (before PyType_Ready(tuple) is called) # (before PyType_Ready(tuple) is called)
type.mro(tuple) type.mro(tuple)
def mutable_bases(): def test_mutable_bases():
# stuff that should work: # stuff that should work:
class C(object): class C(object):
pass pass
...@@ -3523,6 +3523,80 @@ def mutable_bases(): ...@@ -3523,6 +3523,80 @@ def mutable_bases():
else: else:
raise TestFailed, "new-style class must have a new-style base" 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(): def mutable_names():
class C(object): class C(object):
pass pass
...@@ -3608,8 +3682,11 @@ def test_main(): ...@@ -3608,8 +3682,11 @@ def test_main():
slotmultipleinheritance() slotmultipleinheritance()
testrmul() testrmul()
testipow() testipow()
mutable_bases() test_mutable_bases()
test_mutable_bases_with_failing_mro()
test_mutable_bases_catch_mro_conflict()
mutable_names() mutable_names()
if verbose: print "All OK" if verbose: print "All OK"
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -142,11 +142,11 @@ static void remove_subclass(PyTypeObject *, PyTypeObject *); ...@@ -142,11 +142,11 @@ static void remove_subclass(PyTypeObject *, PyTypeObject *);
static void update_all_slots(PyTypeObject *); static void update_all_slots(PyTypeObject *);
static int static int
mro_subclasses(PyTypeObject *type) mro_subclasses(PyTypeObject *type, PyObject* temp)
{ {
PyTypeObject *subclass; PyTypeObject *subclass;
PyObject *ref, *subclasses, *old_mro; PyObject *ref, *subclasses, *old_mro;
int i, n, r = 0; int i, n;
subclasses = type->tp_subclasses; subclasses = type->tp_subclasses;
if (subclasses == NULL) if (subclasses == NULL)
...@@ -164,22 +164,27 @@ mro_subclasses(PyTypeObject *type) ...@@ -164,22 +164,27 @@ mro_subclasses(PyTypeObject *type)
old_mro = subclass->tp_mro; old_mro = subclass->tp_mro;
if (mro_internal(subclass) < 0) { if (mro_internal(subclass) < 0) {
subclass->tp_mro = old_mro; subclass->tp_mro = old_mro;
r = -1; return -1;
} }
else { 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) if (mro_subclasses(subclass, temp) < 0)
r = -1; return -1;
} }
return r; return 0;
} }
static int static int
type_set_bases(PyTypeObject *type, PyObject *value, void *context) type_set_bases(PyTypeObject *type, PyObject *value, void *context)
{ {
int i, r = 0; int i, r = 0;
PyObject* ob; PyObject *ob, *temp;
PyTypeObject *new_base, *old_base; PyTypeObject *new_base, *old_base;
PyObject *old_bases, *old_mro; PyObject *old_bases, *old_mro;
...@@ -247,8 +252,25 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context) ...@@ -247,8 +252,25 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context)
return -1; return -1;
} }
if (mro_subclasses(type) < 0) temp = PyList_New(0);
r = -1;
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 /* any base that was in __bases__ but now isn't, we
need to remove |type| from it's tp_subclasses. 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