summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2001-09-18 03:53:24 (GMT)
committerGuido van Rossum <guido@python.org>2001-09-18 03:53:24 (GMT)
commitf0b35e1501f279aba1b97df068c6bc7384e2ab1a (patch)
treee1cf13b51b0275e6069e340eeaa02152b0eb5e75 /Objects
parentbd131497112da319c4361c3e8656b5b7055011e3 (diff)
downloadcpython-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.c106
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 */