Redo the PyMethod attributes using a dir()-friendly approach, creating
descriptors for each attribute.  The getattr() implementation is
similar to PyObject_GenericGetAttr(), but delegates to im_self instead
of looking in __dict__; I couldn't do this as a wrapper around
PyObject_GenericGetAttr().

XXX A problem here is that this is a case of *delegation*.  dir()
doesn't see exactly the same attributes that are actually defined;
e.g. if the delegate is a Python function object, it supports
attributes like func_code etc., but these are not visible to dir(); on
the other hand, dynamic function attributes (stored in the function's
__dict__) *are* visible to dir().  Maybe we need a mechanism to tell
dir() about the delegation mechanism?  I vaguely recall seeing a
request in the newsgroup for a more formal definition of attribute
delegation too.  Sigh, time for a new PEP.
diff --git a/Objects/classobject.c b/Objects/classobject.c
index 9d84173..25a0743 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -2000,58 +2000,90 @@
 	return (PyObject *)im;
 }
 
-/* Class method methods */
+/* Descriptors for PyMethod attributes */
+
+/* im_class, im_func and im_self are stored in the PyMethod object */
 
 #define OFF(x) offsetof(PyMethodObject, x)
 
 static struct memberlist instancemethod_memberlist[] = {
-	{"im_func",	T_OBJECT,	OFF(im_func)},
-	{"im_self",	T_OBJECT,	OFF(im_self)},
-	{"im_class",	T_OBJECT,	OFF(im_class)},
-	/* Dummies that are not handled by getattr() except for __members__ */
-	{"__doc__",	T_INT,		0},
-	{"__name__",	T_INT,		0},
-	{"__dict__",    T_OBJECT,       0},
+	{"im_class",	T_OBJECT,	OFF(im_class),	READONLY|RESTRICTED},
+	{"im_func",	T_OBJECT,	OFF(im_func),	READONLY|RESTRICTED},
+	{"im_self",	T_OBJECT,	OFF(im_self),	READONLY|RESTRICTED},
 	{NULL}	/* Sentinel */
 };
 
-static int
-instancemethod_setattro(register PyMethodObject *im, PyObject *name,
-			PyObject *v)
-{
-	char *sname = PyString_AsString(name);
-
-	PyErr_Format(PyExc_TypeError, "read-only attribute: %s", sname);
-	return -1;
-}
- 
+/* __dict__, __doc__ and __name__ are retrieved from im_func */
 
 static PyObject *
-instancemethod_getattro(register PyMethodObject *im, PyObject *name)
+im_get_dict(PyMethodObject *im)
 {
-	PyObject *rtn;
-	char *sname = PyString_AsString(name);
-	if (sname[0] == '_') {
-		/* Inherit __name__ and __doc__ from the callable object
-		   implementing the method */
-	        if (strcmp(sname, "__name__") == 0 ||
-		    strcmp(sname, "__doc__") == 0)
-			return PyObject_GetAttr(im->im_func, name);
-	}
-	if (PyEval_GetRestricted()) {
-		PyErr_SetString(PyExc_RuntimeError,
-	    "instance-method attributes not accessible in restricted mode");
-		return NULL;
-	}
-	if (sname[0] == '_' && strcmp(sname, "__dict__") == 0)
-		return PyObject_GetAttr(im->im_func, name);
+	return PyObject_GetAttrString(im->im_func, "__dict__");
+}
 
-	rtn = PyMember_Get((char *)im, instancemethod_memberlist, sname);
-	if (rtn == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
-		PyErr_Clear();
-		rtn = PyObject_GetAttr(im->im_func, name);
+static PyObject *
+im_get_doc(PyMethodObject *im)
+{
+	return PyObject_GetAttrString(im->im_func, "__doc__");
+}
+
+static PyObject *
+im_get_name(PyMethodObject *im)
+{
+	return PyObject_GetAttrString(im->im_func, "__name__");
+}
+
+static struct getsetlist instancemethod_getsetlist[] = {
+	{"__dict__", (getter)im_get_dict},
+	{"__doc__", (getter)im_get_doc},
+	{"__name__", (getter)im_get_name},
+	{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. */
+
+static PyObject *
+instancemethod_getattro(PyObject *obj, PyObject *name)
+{
+	PyMethodObject *im = (PyMethodObject *)obj;
+	PyTypeObject *tp = obj->ob_type;
+	PyObject *descr, *res;
+	descrgetfunc f;
+
+	if (tp->tp_dict == NULL) {
+		if (PyType_Ready(tp) < 0)
+			return NULL;
 	}
-	return rtn;
+
+	descr = _PyType_Lookup(tp, name);
+	f = NULL;
+	if (descr != NULL) {
+		f = descr->ob_type->tp_descr_get;
+		if (f != NULL && PyDescr_IsData(descr))
+			return f(descr, obj, (PyObject *)obj->ob_type);
+	}
+
+	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;
 }
 
 static void
@@ -2298,7 +2330,7 @@
 	instancemethod_call,			/* tp_call */
 	0,					/* tp_str */
 	(getattrofunc)instancemethod_getattro,	/* tp_getattro */
-	(setattrofunc)instancemethod_setattro,	/* tp_setattro */
+	PyObject_GenericSetAttr,		/* tp_setattro */
 	0,					/* tp_as_buffer */
 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
 	0,					/* tp_doc */
@@ -2309,8 +2341,8 @@
 	0,					/* tp_iter */
 	0,					/* tp_iternext */
 	0,					/* tp_methods */
-	0,					/* tp_members */
-	0,					/* tp_getset */
+	instancemethod_memberlist,		/* tp_members */
+	instancemethod_getsetlist,		/* tp_getset */
 	0,					/* tp_base */
 	0,					/* tp_dict */
 	instancemethod_descr_get,		/* tp_descr_get */