Another step in the right direction: when a new class's attribute

corresponding to a dispatch slot (e.g. __getitem__ or __add__) is set,
calculate the proper dispatch slot and propagate the change to all
subclasses.  Because of multiple inheritance, there's no easy way to
avoid always recursing down the tree of subclasses.  Who cares?

(There's more to do, but this works.  There's also a test for this now.)
This commit is contained in:
Guido van Rossum 2001-10-11 18:33:53 +00:00
parent 9e4ca10ce4
commit 875eeaa193
2 changed files with 195 additions and 49 deletions

View File

@ -7,6 +7,10 @@ def vereq(a, b):
if not (a == b):
raise TestFailed, "%r == %r" % (a, b)
def veris(a, b):
if a is not b:
raise TestFailed, "%r is %r" % (a, b)
def testunop(a, res, expr="len(a)", meth="__len__"):
if verbose: print "checking", expr
dict = {'a': a}
@ -623,10 +627,8 @@ def metaclass():
class autosuper(type):
# Automatically add __super to the class
# This trick only works for dynamic classes
# so we force __dynamic__ = 1
def __new__(metaclass, name, bases, dict):
# XXX Should check that name isn't already a base class name
dict["__dynamic__"] = 1
assert dict.get("__dynamic__", 1)
cls = super(autosuper, metaclass).__new__(metaclass,
name, bases, dict)
# Name mangling for __super removes leading underscores
@ -949,7 +951,7 @@ def dynamics():
# Test handling of int*seq and seq*int
class I(int):
__dynamic__ = 1
__dynamic__ = 1 # XXX why?
vereq("a"*I(2), "aa")
vereq(I(2)*"a", "aa")
vereq(2*I(3), 6)
@ -958,7 +960,7 @@ def dynamics():
# Test handling of long*seq and seq*long
class L(long):
__dynamic__ = 1
__dynamic__ = 1 # XXX why?
vereq("a"*L(2L), "aa")
vereq(L(2L)*"a", "aa")
vereq(2*L(3), 6)
@ -967,7 +969,7 @@ def dynamics():
# Test comparison of classes with dynamic metaclasses
class dynamicmetaclass(type):
__dynamic__ = 1
__dynamic__ = 1 # XXX ???
class someclass:
__metaclass__ = dynamicmetaclass
verify(someclass != object)
@ -1253,7 +1255,7 @@ def specials():
verify(10 not in c1)
# Test the default behavior for dynamic classes
class D(object):
__dynamic__ = 1
__dynamic__ = 1 # XXX why?
def __getitem__(self, i):
if 0 <= i < 10: return i
raise IndexError
@ -1563,30 +1565,30 @@ def inherits():
verify((+a).__class__ is float)
class madcomplex(complex):
__dynamic__ = 0
__dynamic__ = 0 # XXX Shouldn't be necessary
def __repr__(self):
return "%.17gj%+.17g" % (self.imag, self.real)
a = madcomplex(-3, 4)
vereq(repr(a), "4j-3")
base = complex(-3, 4)
verify(base.__class__ is complex)
veris(base.__class__, complex)
vereq(a, base)
vereq(complex(a), base)
verify(complex(a).__class__ is complex)
veris(complex(a).__class__, complex)
a = madcomplex(a) # just trying another form of the constructor
vereq(repr(a), "4j-3")
vereq(a, base)
vereq(complex(a), base)
verify(complex(a).__class__ is complex)
veris(complex(a).__class__, complex)
vereq(hash(a), hash(base))
verify((+a).__class__ is complex)
verify((a + 0).__class__ is complex)
veris((+a).__class__, complex)
veris((a + 0).__class__, complex)
vereq(a + 0, base)
verify((a - 0).__class__ is complex)
veris((a - 0).__class__, complex)
vereq(a - 0, base)
verify((a * 1).__class__ is complex)
veris((a * 1).__class__, complex)
vereq(a * 1, base)
verify((a / 1).__class__ is complex)
veris((a / 1).__class__, complex)
vereq(a / 1, base)
class madtuple(tuple):
@ -2237,6 +2239,66 @@ def binopoverride():
def __eq__(self, other):
return self.lower() == other.lower()
def subclasspropagation():
if verbose: print "Testing propagation of slot functions to subclasses..."
class A(object):
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
d = D()
vereq(hash(d), id(d))
A.__hash__ = lambda self: 42
vereq(hash(d), 42)
C.__hash__ = lambda self: 314
vereq(hash(d), 314)
B.__hash__ = lambda self: 144
vereq(hash(d), 144)
D.__hash__ = lambda self: 100
vereq(hash(d), 100)
del D.__hash__
vereq(hash(d), 144)
del B.__hash__
vereq(hash(d), 314)
del C.__hash__
vereq(hash(d), 42)
del A.__hash__
vereq(hash(d), id(d))
d.foo = 42
d.bar = 42
vereq(d.foo, 42)
vereq(d.bar, 42)
def __getattribute__(self, name):
if name == "foo":
return 24
return object.__getattribute__(self, name)
A.__getattribute__ = __getattribute__
vereq(d.foo, 24)
vereq(d.bar, 42)
def __getattr__(self, name):
if name in ("spam", "foo", "bar"):
return "hello"
raise AttributeError, name
B.__getattr__ = __getattr__
vereq(d.spam, "hello")
vereq(d.foo, 24)
vereq(d.bar, 42)
del A.__getattribute__
vereq(d.foo, 42)
del d.foo
vereq(d.foo, "hello")
vereq(d.bar, 42)
del B.__getattr__
try:
d.foo
except AttributeError:
pass
else:
raise TestFailed, "d.foo should be undefined now"
def test_main():
class_docstrings()
@ -2284,6 +2346,7 @@ def test_main():
pickles()
copies()
binopoverride()
subclasspropagation()
if verbose: print "All OK"
if __name__ == "__main__":

View File

@ -676,7 +676,7 @@ solid_base(PyTypeObject *type)
staticforward void object_dealloc(PyObject *);
staticforward int object_init(PyObject *, PyObject *, PyObject *);
staticforward int update_slot(PyTypeObject *, PyObject *, PyObject *);
staticforward int update_slot(PyTypeObject *, PyObject *);
staticforward void fixup_slot_dispatchers(PyTypeObject *);
static PyObject *
@ -1107,7 +1107,7 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) {
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
return -1;
return update_slot(type, name, value);
return update_slot(type, name);
}
PyErr_SetString(PyExc_TypeError, "can't set static type attributes");
return -1;
@ -3679,6 +3679,7 @@ typedef struct {
int offset;
void *function;
wrapperfunc wrapper;
PyObject *name_strobj;
} slotdef;
#undef TPSLOT
@ -3797,9 +3798,7 @@ static slotdef slotdefs[] = {
slot_nb_inplace_true_divide, wrap_binaryfunc),
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc),
TPSLOT("__str__", tp_print, NULL, NULL),
TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc),
TPSLOT("__repr__", tp_print, NULL, NULL),
TPSLOT("__cmp__", tp_compare, _PyObject_SlotCompare, wrap_cmpfunc),
TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc),
TPSLOT("__call__", tp_call, slot_tp_call, wrap_call),
@ -3827,35 +3826,6 @@ static slotdef slotdefs[] = {
{NULL}
};
static int
update_slot(PyTypeObject *type, PyObject *name, PyObject *value)
{
char *s;
int n;
slotdef *p;
void **ptr;
assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
if (value == NULL)
return 0; /* Can't unset a slot */
s = PyString_AsString(name);
n = PyString_Size(name);
if (s == NULL || n < 0) {
/* Shouldn't happen, but can't be bothered */
PyErr_Clear();
return 0;
}
if (!(s[0] == '_' && s[1] == '_' && s[n-1] == '_' && s[n-2] == '_'))
return 0;
for (p = slotdefs; p->name; p++) {
if (!strcmp(p->name, s)) {
ptr = (void **) ((char *)type + p->offset);
*ptr = p->function;
}
}
return 0;
}
static void **
slotptr(PyTypeObject *type, int offset)
{
@ -3883,6 +3853,119 @@ slotptr(PyTypeObject *type, int offset)
return (void **)ptr;
}
staticforward int recurse_down_subclasses(PyTypeObject *type, int offset);
static int
update_one_slot(PyTypeObject *type, int offset)
{
slotdef *p;
PyObject *descr;
PyWrapperDescrObject *d;
void *generic = NULL, *specific = NULL;
int use_generic = 0;
void **ptr;
for (p = slotdefs; p->name; p++) {
if (p->offset != offset)
continue;
descr = _PyType_Lookup(type, p->name_strobj);
if (descr == NULL)
continue;
ptr = slotptr(type, p->offset);
if (ptr == NULL)
continue;
generic = p->function;
if (descr->ob_type == &PyWrapperDescr_Type) {
d = (PyWrapperDescrObject *)descr;
if (d->d_base->wrapper == p->wrapper &&
PyType_IsSubtype(type, d->d_type)) {
if (specific == NULL ||
specific == d->d_wrapped)
specific = d->d_wrapped;
else
use_generic = 1;
}
}
else
use_generic = 1;
if (specific && !use_generic)
*ptr = specific;
else
*ptr = generic;
}
if (recurse_down_subclasses(type, offset) < 0)
return -1;
return 0;
}
static int
recurse_down_subclasses(PyTypeObject *type, int offset)
{
PyTypeObject *subclass;
PyObject *ref, *subclasses;
int i, n;
subclasses = type->tp_subclasses;
if (subclasses == NULL)
return 0;
assert(PyList_Check(subclasses));
n = PyList_GET_SIZE(subclasses);
for (i = 0; i < n; i++) {
ref = PyList_GET_ITEM(subclasses, i);
assert(PyWeakref_CheckRef(ref));
subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref);
if (subclass == NULL)
continue;
assert(PyType_Check(subclass));
if (update_one_slot(subclass, offset) < 0)
return -1;
}
return 0;
}
static void
init_name_strobj(void)
{
slotdef *p;
static int initialized = 0;
if (initialized)
return;
for (p = slotdefs; p->name; p++) {
p->name_strobj = PyString_InternFromString(p->name);
if (!p->name_strobj)
Py_FatalError("XXX ouch");
}
initialized = 1;
}
static void
collect_offsets(PyObject *name, int offsets[])
{
slotdef *p;
init_name_strobj();
for (p = slotdefs; p->name; p++) {
if (name == p->name_strobj)
*offsets++ = p->offset;
}
*offsets = 0;
}
static int
update_slot(PyTypeObject *type, PyObject *name)
{
int offsets[10];
int *ip;
collect_offsets(name, offsets);
for (ip = offsets; *ip; ip++) {
if (update_one_slot(type, *ip) < 0)
return -1;
}
return 0;
}
static void
fixup_slot_dispatchers(PyTypeObject *type)
{