diff options
-rw-r--r-- | Lib/test/test_descr.py | 95 | ||||
-rw-r--r-- | Objects/typeobject.c | 149 |
2 files changed, 195 insertions, 49 deletions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 50e7d9d..1768a6d 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -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__": diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1b51971..ced80ec 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -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) { |