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.
This commit is contained in:
Guido van Rossum 2001-09-18 03:53:24 +00:00
parent bd13149711
commit f0b35e1501
1 changed files with 75 additions and 43 deletions

View File

@ -2000,58 +2000,90 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *class)
return (PyObject *)im; 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) #define OFF(x) offsetof(PyMethodObject, x)
static struct memberlist instancemethod_memberlist[] = { static struct memberlist instancemethod_memberlist[] = {
{"im_func", T_OBJECT, OFF(im_func)}, {"im_class", T_OBJECT, OFF(im_class), READONLY|RESTRICTED},
{"im_self", T_OBJECT, OFF(im_self)}, {"im_func", T_OBJECT, OFF(im_func), READONLY|RESTRICTED},
{"im_class", T_OBJECT, OFF(im_class)}, {"im_self", T_OBJECT, OFF(im_self), READONLY|RESTRICTED},
/* Dummies that are not handled by getattr() except for __members__ */
{"__doc__", T_INT, 0},
{"__name__", T_INT, 0},
{"__dict__", T_OBJECT, 0},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static int /* __dict__, __doc__ and __name__ are retrieved from im_func */
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;
}
static PyObject * static PyObject *
instancemethod_getattro(register PyMethodObject *im, PyObject *name) im_get_dict(PyMethodObject *im)
{ {
PyObject *rtn; return PyObject_GetAttrString(im->im_func, "__dict__");
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);
rtn = PyMember_Get((char *)im, instancemethod_memberlist, sname); static PyObject *
if (rtn == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { im_get_doc(PyMethodObject *im)
PyErr_Clear(); {
rtn = PyObject_GetAttr(im->im_func, name); 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 static void
@ -2298,7 +2330,7 @@ PyTypeObject PyMethod_Type = {
instancemethod_call, /* tp_call */ instancemethod_call, /* tp_call */
0, /* tp_str */ 0, /* tp_str */
(getattrofunc)instancemethod_getattro, /* tp_getattro */ (getattrofunc)instancemethod_getattro, /* tp_getattro */
(setattrofunc)instancemethod_setattro, /* tp_setattro */ PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
@ -2309,8 +2341,8 @@ PyTypeObject PyMethod_Type = {
0, /* tp_iter */ 0, /* tp_iter */
0, /* tp_iternext */ 0, /* tp_iternext */
0, /* tp_methods */ 0, /* tp_methods */
0, /* tp_members */ instancemethod_memberlist, /* tp_members */
0, /* tp_getset */ instancemethod_getsetlist, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
0, /* tp_dict */ 0, /* tp_dict */
instancemethod_descr_get, /* tp_descr_get */ instancemethod_descr_get, /* tp_descr_get */