]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Readjustments to the way we cope with exceptions from subclasses'
authorMichael W. Hudson <mwh@python.net>
Wed, 27 Nov 2002 15:20:19 +0000 (15:20 +0000)
committerMichael W. Hudson <mwh@python.net>
Wed, 27 Nov 2002 15:20:19 +0000 (15:20 +0000)
mro() methods.  Now any exception aborts the whole __bases__ change.

And more tests.

Lib/test/test_descr.py
Objects/typeobject.c

index dde165d251d4bb4576614753a503a619950526c3..19a0ae49241388c2d774cd868d2c30b929ecd18f 100644 (file)
@@ -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__":
index 82237c870cd6d4f27f2b398166c7e837d4508199..949cef58eeeb2a3b2e36c5015134725966e78da2 100644 (file)
@@ -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.