Commit c0ba52d3 authored by Armin Rigo's avatar Armin Rigo

Revert r53997 as per

http://mail.python.org/pipermail/python-dev/2007-March/071796.html .

I've kept a couple of still-valid extra tests in test_descr, but didn't
bother to sort through the new comments and refactorings added in r53997
to see if some of them could be kept.  If so, they could go in a
follow-up check-in.
parent bd53870d
# Test enhancements related to descriptors and new-style classes
from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_original_stdout, run_doctest
from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_original_stdout
from copy import deepcopy
import warnings
......@@ -1466,89 +1466,65 @@ def dynamics():
verify(someclass != object)
def errors():
"""Test that type can't be placed after an instance of type in bases.
>>> class C(list, dict):
... pass
Traceback (most recent call last):
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
>>> class C(object, None):
... pass
Traceback (most recent call last):
TypeError: Error when calling the metaclass bases
bases must be types
>>> class C(type(len)):
... pass
Traceback (most recent call last):
TypeError: Error when calling the metaclass bases
type 'builtin_function_or_method' is not an acceptable base type
>>> class Classic:
... def __init__(*args): pass
>>> class C(object):
... __metaclass__ = Classic
>>> class C(object):
... __slots__ = 1
Traceback (most recent call last):
TypeError: Error when calling the metaclass bases
'int' object is not iterable
>>> class C(object):
... __slots__ = [1]
Traceback (most recent call last):
TypeError: Error when calling the metaclass bases
__slots__ items must be strings, not 'int'
>>> class A(object):
... pass
>>> class B(A, type):
... pass
Traceback (most recent call last):
TypeError: Error when calling the metaclass bases
metaclass conflict: type must occur in bases before other non-classic base classes
Create two different metaclasses in order to setup an error where
there is no inheritance relationship between the metaclass of a class
and the metaclass of its bases.
>>> class M1(type):
... pass
>>> class M2(type):
... pass
>>> class A1(object):
... __metaclass__ = M1
>>> class A2(object):
... __metaclass__ = M2
>>> class B(A1, A2):
... pass
Traceback (most recent call last):
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
>>> class B(A1):
... pass
Also check that assignment to bases is safe.
>>> B.__bases__ = A1, A2
Traceback (most recent call last):
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
>>> B.__bases__ = A2,
Traceback (most recent call last):
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
>>> class M3(M1):
... pass
>>> class C(object):
... __metaclass__ = M3
>>> B.__bases__ = C,
Traceback (most recent call last):
TypeError: assignment to __bases__ may not change metatype
"""
if verbose: print "Testing errors..."
try:
class C(list, dict):
pass
except TypeError:
pass
else:
verify(0, "inheritance from both list and dict should be illegal")
try:
class C(object, None):
pass
except TypeError:
pass
else:
verify(0, "inheritance from non-type should be illegal")
class Classic:
pass
try:
class C(type(len)):
pass
except TypeError:
pass
else:
verify(0, "inheritance from CFunction should be illegal")
try:
class C(object):
__slots__ = 1
except TypeError:
pass
else:
verify(0, "__slots__ = 1 should be illegal")
try:
class C(object):
__slots__ = [1]
except TypeError:
pass
else:
verify(0, "__slots__ = [1] should be illegal")
class M1(type):
pass
class M2(type):
pass
class A1(object):
__metaclass__ = M1
class A2(object):
__metaclass__ = M2
try:
class B(A1, A2):
pass
except TypeError:
pass
else:
verify(0, "finding the most derived metaclass should have failed")
def classmethods():
if verbose: print "Testing class methods..."
......@@ -4331,6 +4307,7 @@ def test_main():
slots()
slotspecials()
dynamics()
errors()
classmethods()
classmethods_in_c()
staticmethods()
......@@ -4399,9 +4376,6 @@ def test_main():
notimplemented()
test_assign_slice()
from test import test_descr
run_doctest(test_descr, verbosity=True)
if verbose: print "All OK"
if __name__ == "__main__":
......
......@@ -127,7 +127,6 @@ type_get_bases(PyTypeObject *type, void *context)
return type->tp_bases;
}
static PyTypeObject *most_derived_metaclass(PyTypeObject *, PyObject *);
static PyTypeObject *best_base(PyObject *);
static int mro_internal(PyTypeObject *);
static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, char *);
......@@ -188,7 +187,7 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context)
Py_ssize_t i;
int r = 0;
PyObject *ob, *temp;
PyTypeObject *new_base, *old_base, *metatype;
PyTypeObject *new_base, *old_base;
PyObject *old_bases, *old_mro;
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
......@@ -231,17 +230,6 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context)
}
}
metatype = most_derived_metaclass(type->ob_type, value);
if (metatype == NULL)
return -1;
if (metatype != type->ob_type) {
PyErr_SetString(PyExc_TypeError,
"assignment to __bases__ may not change "
"metatype");
return -1;
}
new_base = best_base(value);
if (!new_base) {
......@@ -1367,14 +1355,7 @@ mro_internal(PyTypeObject *type)
/* Calculate the best base amongst multiple base classes.
This is the first one that's on the path to the "solid base".
Requires that all base classes be types or classic classes.
Will return NULL with TypeError set if
1) the base classes have conflicting layout instances, or
2) all the bases are classic classes.
*/
This is the first one that's on the path to the "solid base". */
static PyTypeObject *
best_base(PyObject *bases)
......@@ -1392,7 +1373,12 @@ best_base(PyObject *bases)
base_proto = PyTuple_GET_ITEM(bases, i);
if (PyClass_Check(base_proto))
continue;
assert(PyType_Check(base_proto));
if (!PyType_Check(base_proto)) {
PyErr_SetString(
PyExc_TypeError,
"bases must be types");
return NULL;
}
base_i = (PyTypeObject *)base_proto;
if (base_i->tp_dict == NULL) {
if (PyType_Ready(base_i) < 0)
......@@ -1445,8 +1431,6 @@ extra_ivars(PyTypeObject *type, PyTypeObject *base)
return t_size != b_size;
}
/* Return the type object that will determine the layout of the instance. */
static PyTypeObject *
solid_base(PyTypeObject *type)
{
......@@ -1462,71 +1446,6 @@ solid_base(PyTypeObject *type)
return base;
}
/* Determine the proper metatype to deal with this, and check some
error cases while we're at it. Note that if some other metatype
wins to contract, it's possible that its instances are not types.
Error cases of interest: 1. The metaclass is not a subclass of a
base class. 2. A non-type, non-classic base class appears before
type.
*/
static PyTypeObject *
most_derived_metaclass(PyTypeObject *metatype, PyObject *bases)
{
Py_ssize_t nbases, i;
PyTypeObject *winner;
/* types_ordered: One of three states possible:
0 type is in bases
1 non-types also in bases
2 type follows non-type in bases (error)
*/
int types_ordered = 0;
nbases = PyTuple_GET_SIZE(bases);
winner = metatype;
for (i = 0; i < nbases; i++) {
PyObject *tmp = PyTuple_GET_ITEM(bases, i);
PyTypeObject *tmptype = tmp->ob_type;
if (tmptype == &PyClass_Type)
continue; /* Special case classic classes */
if (!PyType_Check(tmp)) {
PyErr_SetString(PyExc_TypeError,
"bases must be types");
return NULL;
}
if (PyObject_IsSubclass(tmp, (PyObject*)&PyType_Type)) {
if (types_ordered == 1) {
types_ordered = 2;
}
}
else if (!types_ordered)
types_ordered = 1;
if (winner == tmptype)
continue;
if (PyType_IsSubtype(winner, tmptype))
continue;
if (PyType_IsSubtype(tmptype, winner)) {
winner = tmptype;
continue;
}
PyErr_SetString(PyExc_TypeError,
"metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases");
return NULL;
}
if (types_ordered == 2) {
PyErr_SetString(PyExc_TypeError,
"metaclass conflict: "
"type must occur in bases before other "
"non-classic base classes");
return NULL;
}
return winner;
}
static void object_dealloc(PyObject *);
static int object_init(PyObject *, PyObject *, PyObject *);
static int update_slot(PyTypeObject *, PyObject *);
......@@ -1760,18 +1679,37 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
&PyDict_Type, &dict))
return NULL;
winner = most_derived_metaclass(metatype, bases);
if (winner == NULL)
/* Determine the proper metatype to deal with this,
and check for metatype conflicts while we're at it.
Note that if some other metatype wins to contract,
it's possible that its instances are not types. */
nbases = PyTuple_GET_SIZE(bases);
winner = metatype;
for (i = 0; i < nbases; i++) {
tmp = PyTuple_GET_ITEM(bases, i);
tmptype = tmp->ob_type;
if (tmptype == &PyClass_Type)
continue; /* Special case classic classes */
if (PyType_IsSubtype(winner, tmptype))
continue;
if (PyType_IsSubtype(tmptype, winner)) {
winner = tmptype;
continue;
}
PyErr_SetString(PyExc_TypeError,
"metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases");
return NULL;
}
if (winner != metatype) {
if (winner->tp_new != type_new) /* Pass it to the winner */ {
if (winner->tp_new != type_new) /* Pass it to the winner */
return winner->tp_new(winner, args, kwds);
}
metatype = winner;
}
/* Adjust for empty tuple bases */
nbases = PyTuple_GET_SIZE(bases);
if (nbases == 0) {
bases = PyTuple_Pack(1, &PyBaseObject_Type);
if (bases == NULL)
......
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