]> granicus.if.org Git - python/commitdiff
Redo the PyMethod attributes using a dir()-friendly approach, creating
authorGuido van Rossum <guido@python.org>
Tue, 18 Sep 2001 03:53:24 +0000 (03:53 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 18 Sep 2001 03:53:24 +0000 (03:53 +0000)
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.

Objects/classobject.c

index 9d84173b0b38e3d13d156691a82be652f69dbb16..25a0743a3cf73fc17f6bc5fb5101068160484f55 100644 (file)
@@ -2000,58 +2000,90 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *class)
        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)
+/* __dict__, __doc__ and __name__ are retrieved from im_func */
+
+static PyObject *
+im_get_dict(PyMethodObject *im)
 {
-       char *sname = PyString_AsString(name);
+       return PyObject_GetAttrString(im->im_func, "__dict__");
+}
+
+static PyObject *
+im_get_doc(PyMethodObject *im)
+{
+       return PyObject_GetAttrString(im->im_func, "__doc__");
+}
 
-       PyErr_Format(PyExc_TypeError, "read-only attribute: %s", sname);
-       return -1;
+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(register PyMethodObject *im, PyObject *name)
+instancemethod_getattro(PyObject *obj, PyObject *name)
 {
-       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);
+       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;
        }
-       if (PyEval_GetRestricted()) {
-               PyErr_SetString(PyExc_RuntimeError,
-           "instance-method attributes not accessible in restricted mode");
-               return NULL;
+
+       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 (sname[0] == '_' && strcmp(sname, "__dict__") == 0)
-               return PyObject_GetAttr(im->im_func, name);
 
-       rtn = PyMember_Get((char *)im, instancemethod_memberlist, sname);
-       if (rtn == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+       if (descr != NULL) {
                PyErr_Clear();
-               rtn = PyObject_GetAttr(im->im_func, name);
+               Py_INCREF(descr);
+               return descr;
        }
-       return rtn;
+
+       assert(PyErr_Occurred());
+       return NULL;
 }
 
 static void
@@ -2298,7 +2330,7 @@ PyTypeObject PyMethod_Type = {
        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 @@ PyTypeObject PyMethod_Type = {
        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 */