summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_descr.py95
-rw-r--r--Objects/typeobject.c149
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)
{