diff options
author | Guido van Rossum <guido@python.org> | 2001-09-18 03:53:24 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2001-09-18 03:53:24 (GMT) |
commit | f0b35e1501f279aba1b97df068c6bc7384e2ab1a (patch) | |
tree | e1cf13b51b0275e6069e340eeaa02152b0eb5e75 /Objects | |
parent | bd131497112da319c4361c3e8656b5b7055011e3 (diff) | |
download | cpython-f0b35e1501f279aba1b97df068c6bc7384e2ab1a.zip cpython-f0b35e1501f279aba1b97df068c6bc7384e2ab1a.tar.gz cpython-f0b35e1501f279aba1b97df068c6bc7384e2ab1a.tar.bz2 |
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.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/classobject.c | 106 |
1 files changed, 69 insertions, 37 deletions
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 @@ 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 */ |