From 586da8fddd6bcf5dd7a2e6a99394f218424a87ca Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Wed, 27 Nov 2002 15:20:19 +0000 Subject: Readjustments to the way we cope with exceptions from subclasses' mro() methods. Now any exception aborts the whole __bases__ change. And more tests. --- Lib/test/test_descr.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++-- Objects/typeobject.c | 42 +++++++++++++++++++------- 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index dde165d..19a0ae4 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -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__": diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 82237c8..949cef5 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -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. -- cgit v0.12