=== modified file 'Lib/test/test_property.py' --- Lib/test/test_property.py 2010-02-23 00:24:49 +0000 +++ Lib/test/test_property.py 2010-04-25 14:32:26 +0000 @@ -128,6 +128,42 @@ self.assertEqual(newgetter.spam, 8) self.assertEqual(newgetter.__class__.spam.__doc__, "new docstring") + def test_super_getter(self): + """Access the getter of a property via super()""" + class Derived(BaseClass): + @property + def spam(self): + return super(Derived, self).spam * 2 + derived = Derived() + self.assertEqual(super(Derived, derived).spam, 5) + self.assertEqual(derived.spam, 10) + # XXX: I would expect this to work as well. Why doesn't it? + # self.assertEqual(super(BaseClass, Derived).spam == BaseClass.spam) + + def test_super_setter(self): + """Access the setter of a property via super()""" + class Derived(BaseClass): + def __init__(self): + super(Derived, self).__init__() + self._augment = list() + @property + def spam(self): + return [super(Derived, self).spam] + self._augment + @spam.setter + def spam(self, value): + if isinstance(value, list): + super(Derived, self).spam = value[0] + self._augment = value[1:] + else: + super(Derived, self).spam = value + self._augment = list() + + derived = Derived() + l = [4, 7, 10] + derived.spam = l + self.assertEqual(derived._spam, 4) + self.assertEqual(derived._augment, [7, 10]) + self.assertEqual(derived.spam, l) # Issue 5890: subclasses of property do not preserve method __doc__ strings class PropertySub(property): @@ -222,8 +258,6 @@ return 2 self.assertEqual(Foo.spam.__doc__, "a new docstring") - - def test_main(): run_unittest(PropertyTests, PropertySubclassTests) === modified file 'Objects/typeobject.c' --- Objects/typeobject.c 2010-04-02 11:30:56 +0000 +++ Objects/typeobject.c 2010-04-25 14:32:26 +0000 @@ -6524,6 +6524,65 @@ return PyObject_GenericGetAttr(self, name); } +static int +super_setattro(PyObject *self, PyObject *name, PyObject *value) +{ + superobject *su = (superobject *)self; + int skip = su->obj_type == NULL; + + if (!skip) { + PyObject *mro, *res, *tmp, *dict; + PyTypeObject *starttype; + descrsetfunc f; + Py_ssize_t i, n; + + starttype = su->obj_type; + mro = starttype->tp_mro; + + if (mro == NULL) + n = 0; + else { + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + } + for (i = 0; i < n; i++) { + if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i)) + break; + } + i++; + res = NULL; + for (; i < n; i++) { + tmp = PyTuple_GET_ITEM(mro, i); + if (PyType_Check(tmp)) + dict = ((PyTypeObject *)tmp)->tp_dict; + else if (PyClass_Check(tmp)) + dict = ((PyClassObject *)tmp)->cl_dict; + else + continue; + res = PyDict_GetItem(dict, name); + if (res != NULL) { + Py_INCREF(res); + f = Py_TYPE(res)->tp_descr_set; + if (f != NULL) { + int t = f(res, + /* Only pass 'obj' param if + this is instance-mode super + (See SF ID #743627) + */ + (su->obj == (PyObject *) + su->obj_type + ? (PyObject *)NULL + : su->obj), + value); + Py_DECREF(res); + return t; + } + } + } + } + return PyObject_GenericSetAttr(self, name, value); +} + static PyTypeObject * supercheck(PyTypeObject *type, PyObject *obj) { @@ -6689,7 +6748,7 @@ 0, /* tp_call */ 0, /* tp_str */ super_getattro, /* tp_getattro */ - 0, /* tp_setattro */ + super_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */