Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f0b35e1

Browse files
committed
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.
1 parent bd13149 commit f0b35e1

1 file changed

Lines changed: 69 additions & 37 deletions

File tree

Objects/classobject.c

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,58 +2000,90 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *class)
20002000
return (PyObject *)im;
20012001
}
20022002

2003-
/* Class method methods */
2003+
/* Descriptors for PyMethod attributes */
2004+
2005+
/* im_class, im_func and im_self are stored in the PyMethod object */
20042006

20052007
#define OFF(x) offsetof(PyMethodObject, x)
20062008

20072009
static struct memberlist instancemethod_memberlist[] = {
2008-
{"im_func", T_OBJECT, OFF(im_func)},
2009-
{"im_self", T_OBJECT, OFF(im_self)},
2010-
{"im_class", T_OBJECT, OFF(im_class)},
2011-
/* Dummies that are not handled by getattr() except for __members__ */
2012-
{"__doc__", T_INT, 0},
2013-
{"__name__", T_INT, 0},
2014-
{"__dict__", T_OBJECT, 0},
2010+
{"im_class", T_OBJECT, OFF(im_class), READONLY|RESTRICTED},
2011+
{"im_func", T_OBJECT, OFF(im_func), READONLY|RESTRICTED},
2012+
{"im_self", T_OBJECT, OFF(im_self), READONLY|RESTRICTED},
20152013
{NULL} /* Sentinel */
20162014
};
20172015

2018-
static int
2019-
instancemethod_setattro(register PyMethodObject *im, PyObject *name,
2020-
PyObject *v)
2016+
/* __dict__, __doc__ and __name__ are retrieved from im_func */
2017+
2018+
static PyObject *
2019+
im_get_dict(PyMethodObject *im)
20212020
{
2022-
char *sname = PyString_AsString(name);
2021+
return PyObject_GetAttrString(im->im_func, "__dict__");
2022+
}
2023+
2024+
static PyObject *
2025+
im_get_doc(PyMethodObject *im)
2026+
{
2027+
return PyObject_GetAttrString(im->im_func, "__doc__");
2028+
}
20232029

2024-
PyErr_Format(PyExc_TypeError, "read-only attribute: %s", sname);
2025-
return -1;
2030+
static PyObject *
2031+
im_get_name(PyMethodObject *im)
2032+
{
2033+
return PyObject_GetAttrString(im->im_func, "__name__");
20262034
}
2027-
2035+
2036+
static struct getsetlist instancemethod_getsetlist[] = {
2037+
{"__dict__", (getter)im_get_dict},
2038+
{"__doc__", (getter)im_get_doc},
2039+
{"__name__", (getter)im_get_name},
2040+
{NULL} /* Sentinel */
2041+
};
2042+
2043+
/* The getattr() implementation for PyMethod objects is similar to
2044+
PyObject_GenericGetAttr(), but instead of looking in __dict__ it
2045+
asks im_self for the attribute. Then the error handling is a bit
2046+
different because we want to preserve the exception raised by the
2047+
delegate, unless we have an alternative from our class. */
20282048

20292049
static PyObject *
2030-
instancemethod_getattro(register PyMethodObject *im, PyObject *name)
2050+
instancemethod_getattro(PyObject *obj, PyObject *name)
20312051
{
2032-
PyObject *rtn;
2033-
char *sname = PyString_AsString(name);
2034-
if (sname[0] == '_') {
2035-
/* Inherit __name__ and __doc__ from the callable object
2036-
implementing the method */
2037-
if (strcmp(sname, "__name__") == 0 ||
2038-
strcmp(sname, "__doc__") == 0)
2039-
return PyObject_GetAttr(im->im_func, name);
2052+
PyMethodObject *im = (PyMethodObject *)obj;
2053+
PyTypeObject *tp = obj->ob_type;
2054+
PyObject *descr, *res;
2055+
descrgetfunc f;
2056+
2057+
if (tp->tp_dict == NULL) {
2058+
if (PyType_Ready(tp) < 0)
2059+
return NULL;
20402060
}
2041-
if (PyEval_GetRestricted()) {
2042-
PyErr_SetString(PyExc_RuntimeError,
2043-
"instance-method attributes not accessible in restricted mode");
2044-
return NULL;
2061+
2062+
descr = _PyType_Lookup(tp, name);
2063+
f = NULL;
2064+
if (descr != NULL) {
2065+
f = descr->ob_type->tp_descr_get;
2066+
if (f != NULL && PyDescr_IsData(descr))
2067+
return f(descr, obj, (PyObject *)obj->ob_type);
2068+
}
2069+
2070+
res = PyObject_GetAttr(im->im_func, name);
2071+
if (res != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError))
2072+
return res;
2073+
2074+
if (f != NULL) {
2075+
PyErr_Clear();
2076+
return f(descr, obj, (PyObject *)obj->ob_type);
20452077
}
2046-
if (sname[0] == '_' && strcmp(sname, "__dict__") == 0)
2047-
return PyObject_GetAttr(im->im_func, name);
20482078

2049-
rtn = PyMember_Get((char *)im, instancemethod_memberlist, sname);
2050-
if (rtn == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
2079+
if (descr != NULL) {
20512080
PyErr_Clear();
2052-
rtn = PyObject_GetAttr(im->im_func, name);
2081+
Py_INCREF(descr);
2082+
return descr;
20532083
}
2054-
return rtn;
2084+
2085+
assert(PyErr_Occurred());
2086+
return NULL;
20552087
}
20562088

20572089
static void
@@ -2298,7 +2330,7 @@ PyTypeObject PyMethod_Type = {
22982330
instancemethod_call, /* tp_call */
22992331
0, /* tp_str */
23002332
(getattrofunc)instancemethod_getattro, /* tp_getattro */
2301-
(setattrofunc)instancemethod_setattro, /* tp_setattro */
2333+
PyObject_GenericSetAttr, /* tp_setattro */
23022334
0, /* tp_as_buffer */
23032335
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
23042336
0, /* tp_doc */
@@ -2309,8 +2341,8 @@ PyTypeObject PyMethod_Type = {
23092341
0, /* tp_iter */
23102342
0, /* tp_iternext */
23112343
0, /* tp_methods */
2312-
0, /* tp_members */
2313-
0, /* tp_getset */
2344+
instancemethod_memberlist, /* tp_members */
2345+
instancemethod_getsetlist, /* tp_getset */
23142346
0, /* tp_base */
23152347
0, /* tp_dict */
23162348
instancemethod_descr_get, /* tp_descr_get */

0 commit comments

Comments
 (0)