mirror of
https://github.com/python/cpython.git
synced 2025-07-23 11:15:24 +00:00
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:
parent
9e4ca10ce4
commit
875eeaa193
2 changed files with 195 additions and 49 deletions
|
@ -7,6 +7,10 @@ def vereq(a, b):
|
||||||
if not (a == b):
|
if not (a == b):
|
||||||
raise TestFailed, "%r == %r" % (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__"):
|
def testunop(a, res, expr="len(a)", meth="__len__"):
|
||||||
if verbose: print "checking", expr
|
if verbose: print "checking", expr
|
||||||
dict = {'a': a}
|
dict = {'a': a}
|
||||||
|
@ -623,10 +627,8 @@ def metaclass():
|
||||||
class autosuper(type):
|
class autosuper(type):
|
||||||
# Automatically add __super to the class
|
# Automatically add __super to the class
|
||||||
# This trick only works for dynamic classes
|
# This trick only works for dynamic classes
|
||||||
# so we force __dynamic__ = 1
|
|
||||||
def __new__(metaclass, name, bases, dict):
|
def __new__(metaclass, name, bases, dict):
|
||||||
# XXX Should check that name isn't already a base class name
|
assert dict.get("__dynamic__", 1)
|
||||||
dict["__dynamic__"] = 1
|
|
||||||
cls = super(autosuper, metaclass).__new__(metaclass,
|
cls = super(autosuper, metaclass).__new__(metaclass,
|
||||||
name, bases, dict)
|
name, bases, dict)
|
||||||
# Name mangling for __super removes leading underscores
|
# Name mangling for __super removes leading underscores
|
||||||
|
@ -949,7 +951,7 @@ def dynamics():
|
||||||
|
|
||||||
# Test handling of int*seq and seq*int
|
# Test handling of int*seq and seq*int
|
||||||
class I(int):
|
class I(int):
|
||||||
__dynamic__ = 1
|
__dynamic__ = 1 # XXX why?
|
||||||
vereq("a"*I(2), "aa")
|
vereq("a"*I(2), "aa")
|
||||||
vereq(I(2)*"a", "aa")
|
vereq(I(2)*"a", "aa")
|
||||||
vereq(2*I(3), 6)
|
vereq(2*I(3), 6)
|
||||||
|
@ -958,7 +960,7 @@ def dynamics():
|
||||||
|
|
||||||
# Test handling of long*seq and seq*long
|
# Test handling of long*seq and seq*long
|
||||||
class L(long):
|
class L(long):
|
||||||
__dynamic__ = 1
|
__dynamic__ = 1 # XXX why?
|
||||||
vereq("a"*L(2L), "aa")
|
vereq("a"*L(2L), "aa")
|
||||||
vereq(L(2L)*"a", "aa")
|
vereq(L(2L)*"a", "aa")
|
||||||
vereq(2*L(3), 6)
|
vereq(2*L(3), 6)
|
||||||
|
@ -967,7 +969,7 @@ def dynamics():
|
||||||
|
|
||||||
# Test comparison of classes with dynamic metaclasses
|
# Test comparison of classes with dynamic metaclasses
|
||||||
class dynamicmetaclass(type):
|
class dynamicmetaclass(type):
|
||||||
__dynamic__ = 1
|
__dynamic__ = 1 # XXX ???
|
||||||
class someclass:
|
class someclass:
|
||||||
__metaclass__ = dynamicmetaclass
|
__metaclass__ = dynamicmetaclass
|
||||||
verify(someclass != object)
|
verify(someclass != object)
|
||||||
|
@ -1253,7 +1255,7 @@ def specials():
|
||||||
verify(10 not in c1)
|
verify(10 not in c1)
|
||||||
# Test the default behavior for dynamic classes
|
# Test the default behavior for dynamic classes
|
||||||
class D(object):
|
class D(object):
|
||||||
__dynamic__ = 1
|
__dynamic__ = 1 # XXX why?
|
||||||
def __getitem__(self, i):
|
def __getitem__(self, i):
|
||||||
if 0 <= i < 10: return i
|
if 0 <= i < 10: return i
|
||||||
raise IndexError
|
raise IndexError
|
||||||
|
@ -1563,30 +1565,30 @@ def inherits():
|
||||||
verify((+a).__class__ is float)
|
verify((+a).__class__ is float)
|
||||||
|
|
||||||
class madcomplex(complex):
|
class madcomplex(complex):
|
||||||
__dynamic__ = 0
|
__dynamic__ = 0 # XXX Shouldn't be necessary
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%.17gj%+.17g" % (self.imag, self.real)
|
return "%.17gj%+.17g" % (self.imag, self.real)
|
||||||
a = madcomplex(-3, 4)
|
a = madcomplex(-3, 4)
|
||||||
vereq(repr(a), "4j-3")
|
vereq(repr(a), "4j-3")
|
||||||
base = complex(-3, 4)
|
base = complex(-3, 4)
|
||||||
verify(base.__class__ is complex)
|
veris(base.__class__, complex)
|
||||||
vereq(a, base)
|
vereq(a, base)
|
||||||
vereq(complex(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
|
a = madcomplex(a) # just trying another form of the constructor
|
||||||
vereq(repr(a), "4j-3")
|
vereq(repr(a), "4j-3")
|
||||||
vereq(a, base)
|
vereq(a, base)
|
||||||
vereq(complex(a), base)
|
vereq(complex(a), base)
|
||||||
verify(complex(a).__class__ is complex)
|
veris(complex(a).__class__, complex)
|
||||||
vereq(hash(a), hash(base))
|
vereq(hash(a), hash(base))
|
||||||
verify((+a).__class__ is complex)
|
veris((+a).__class__, complex)
|
||||||
verify((a + 0).__class__ is complex)
|
veris((a + 0).__class__, complex)
|
||||||
vereq(a + 0, base)
|
vereq(a + 0, base)
|
||||||
verify((a - 0).__class__ is complex)
|
veris((a - 0).__class__, complex)
|
||||||
vereq(a - 0, base)
|
vereq(a - 0, base)
|
||||||
verify((a * 1).__class__ is complex)
|
veris((a * 1).__class__, complex)
|
||||||
vereq(a * 1, base)
|
vereq(a * 1, base)
|
||||||
verify((a / 1).__class__ is complex)
|
veris((a / 1).__class__, complex)
|
||||||
vereq(a / 1, base)
|
vereq(a / 1, base)
|
||||||
|
|
||||||
class madtuple(tuple):
|
class madtuple(tuple):
|
||||||
|
@ -2237,6 +2239,66 @@ def binopoverride():
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.lower() == other.lower()
|
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():
|
def test_main():
|
||||||
class_docstrings()
|
class_docstrings()
|
||||||
|
@ -2284,6 +2346,7 @@ def test_main():
|
||||||
pickles()
|
pickles()
|
||||||
copies()
|
copies()
|
||||||
binopoverride()
|
binopoverride()
|
||||||
|
subclasspropagation()
|
||||||
if verbose: print "All OK"
|
if verbose: print "All OK"
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -676,7 +676,7 @@ solid_base(PyTypeObject *type)
|
||||||
|
|
||||||
staticforward void object_dealloc(PyObject *);
|
staticforward void object_dealloc(PyObject *);
|
||||||
staticforward int object_init(PyObject *, PyObject *, 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 *);
|
staticforward void fixup_slot_dispatchers(PyTypeObject *);
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1107,7 +1107,7 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
|
||||||
if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) {
|
if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) {
|
||||||
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
|
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return update_slot(type, name, value);
|
return update_slot(type, name);
|
||||||
}
|
}
|
||||||
PyErr_SetString(PyExc_TypeError, "can't set static type attributes");
|
PyErr_SetString(PyExc_TypeError, "can't set static type attributes");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -3679,6 +3679,7 @@ typedef struct {
|
||||||
int offset;
|
int offset;
|
||||||
void *function;
|
void *function;
|
||||||
wrapperfunc wrapper;
|
wrapperfunc wrapper;
|
||||||
|
PyObject *name_strobj;
|
||||||
} slotdef;
|
} slotdef;
|
||||||
|
|
||||||
#undef TPSLOT
|
#undef TPSLOT
|
||||||
|
@ -3797,9 +3798,7 @@ static slotdef slotdefs[] = {
|
||||||
slot_nb_inplace_true_divide, wrap_binaryfunc),
|
slot_nb_inplace_true_divide, wrap_binaryfunc),
|
||||||
|
|
||||||
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc),
|
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_repr, slot_tp_repr, wrap_unaryfunc),
|
||||||
TPSLOT("__repr__", tp_print, NULL, NULL),
|
|
||||||
TPSLOT("__cmp__", tp_compare, _PyObject_SlotCompare, wrap_cmpfunc),
|
TPSLOT("__cmp__", tp_compare, _PyObject_SlotCompare, wrap_cmpfunc),
|
||||||
TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc),
|
TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc),
|
||||||
TPSLOT("__call__", tp_call, slot_tp_call, wrap_call),
|
TPSLOT("__call__", tp_call, slot_tp_call, wrap_call),
|
||||||
|
@ -3827,35 +3826,6 @@ static slotdef slotdefs[] = {
|
||||||
{NULL}
|
{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 **
|
static void **
|
||||||
slotptr(PyTypeObject *type, int offset)
|
slotptr(PyTypeObject *type, int offset)
|
||||||
{
|
{
|
||||||
|
@ -3883,6 +3853,119 @@ slotptr(PyTypeObject *type, int offset)
|
||||||
return (void **)ptr;
|
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
|
static void
|
||||||
fixup_slot_dispatchers(PyTypeObject *type)
|
fixup_slot_dispatchers(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue