mirror of
https://github.com/python/cpython.git
synced 2025-12-15 21:44:50 +00:00
Readjustments to the way we cope with exceptions from subclasses'
mro() methods. Now any exception aborts the whole __bases__ change. And more tests.
This commit is contained in:
parent
31ddfb6942
commit
586da8fddd
2 changed files with 111 additions and 12 deletions
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue