diff --git a/Misc/NEWS b/Misc/NEWS index 30fe4be67f8..f7b1e96710f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,15 @@ What's New in Python 2.4 alpha 1? Core and builtins ----------------- +- When method objects have an attribute that can be satisfied either + by the function object or by the method object, the function + object's attribute usually wins. Christian Tismer pointed out that + that this is really a mistake, because this only happens for special + methods (like __reduce__) where the method object's version is + really more appropriate than the function's attribute. So from now + on, all method attributes will have precedence over function + attributes with the same name. + - Critical bugfix, for SF bug 839548: if a weakref with a callback, its callback, and its weakly referenced object, all became part of cyclic garbage during a single run of garbage collection, the order diff --git a/Objects/classobject.c b/Objects/classobject.c index 84b297cad95..17a56467817 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -2138,19 +2138,34 @@ static PyMemberDef instancemethod_memberlist[] = { {NULL} /* Sentinel */ }; -/* The getattr() implementation for PyMethod objects is similar to - PyObject_GenericGetAttr(), but instead of looking in __dict__ it - asks im_self for the attribute. Then the error handling is a bit - different because we want to preserve the exception raised by the - delegate, unless we have an alternative from our class. */ +/* Christian Tismer argued convincingly that method attributes should + (nearly) always override function attributes. + The one exception is __doc__; there's a default __doc__ which + should only be used for the class, not for instances */ + +static PyObject * +instancemethod_get_doc(PyMethodObject *im, void *context) +{ + static PyObject *docstr; + if (docstr == NULL) { + docstr= PyString_InternFromString("__doc__"); + if (docstr == NULL) + return NULL; + } + return PyObject_GetAttr(im->im_func, docstr); +} + +static PyGetSetDef instancemethod_getset[] = { + {"__doc__", (getter)instancemethod_get_doc, NULL, NULL}, + {0} +}; static PyObject * instancemethod_getattro(PyObject *obj, PyObject *name) { PyMethodObject *im = (PyMethodObject *)obj; PyTypeObject *tp = obj->ob_type; - PyObject *descr = NULL, *res; - descrgetfunc f = NULL; + PyObject *descr = NULL; if (PyType_HasFeature(tp, Py_TPFLAGS_HAVE_CLASS)) { if (tp->tp_dict == NULL) { @@ -2160,30 +2175,17 @@ instancemethod_getattro(PyObject *obj, PyObject *name) descr = _PyType_Lookup(tp, name); } - f = NULL; if (descr != NULL) { - f = TP_DESCR_GET(descr->ob_type); - if (f != NULL && PyDescr_IsData(descr)) + descrgetfunc f = TP_DESCR_GET(descr->ob_type); + if (f != NULL) return f(descr, obj, (PyObject *)obj->ob_type); + else { + Py_INCREF(descr); + return descr; + } } - res = PyObject_GetAttr(im->im_func, name); - if (res != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) - return res; - - if (f != NULL) { - PyErr_Clear(); - return f(descr, obj, (PyObject *)obj->ob_type); - } - - if (descr != NULL) { - PyErr_Clear(); - Py_INCREF(descr); - return descr; - } - - assert(PyErr_Occurred()); - return NULL; + return PyObject_GetAttr(im->im_func, name); } PyDoc_STRVAR(instancemethod_doc, @@ -2490,7 +2492,7 @@ PyTypeObject PyMethod_Type = { 0, /* tp_iternext */ 0, /* tp_methods */ instancemethod_memberlist, /* tp_members */ - 0, /* tp_getset */ + instancemethod_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ instancemethod_descr_get, /* tp_descr_get */