Committing PEP 232, function attribute feature, approved by Guido.

Closes SF patch #103123.

funcobject.h:

    PyFunctionObject: add the func_dict slot.

funcobject.c:

    PyFunction_New(): Initialize the func_dict slot to NULL.

    func_getattr(): Rename to func_getattro() and change the
    signature.  It's more efficient to use attro methods and dig the C
    string out than it is to re-convert a C string to a PyString.

    Also, add support for getting the __dict__ (a.k.a. func_dict)
    attribute, and for getting an arbitrary function attribute.

    func_setattr(): Rename to func_setattro() and change the signature
    for the same reason.  Also add support for setting __dict__
    (a.k.a. func_dict) and any arbitrary function attribute.

    func_dealloc(): Be sure to DECREF the func_dict slot.

    func_traverse(): Be sure to traverse func_dict too.

    PyFunction_Type: make the necessary func_?etattro() changes.

classobject.c:

    instancemethod_memberlist: Add __dict__

    instancemethod_setattro(): New method to set arbitrary attributes
    on methods (really the underlying im_func).  Raise TypeError when
    the instance is bound or when you're trying to set one of the
    reserved im_* attributes.

    instancemethod_getattr(): Renamed to instancemethod_getattro()
    since that's what it really is.  Also, added support fo getting
    arbitrary attributes through the im_func.

    PyMethod_Type: Do the ?etattr{,o} dance.
This commit is contained in:
Barry Warsaw 2001-01-15 20:40:19 +00:00
parent 4a420a0a75
commit d6a9e84c81
3 changed files with 115 additions and 16 deletions

View File

@ -14,6 +14,7 @@ typedef struct {
PyObject *func_defaults; PyObject *func_defaults;
PyObject *func_doc; PyObject *func_doc;
PyObject *func_name; PyObject *func_name;
PyObject *func_dict;
} PyFunctionObject; } PyFunctionObject;
extern DL_IMPORT(PyTypeObject) PyFunction_Type; extern DL_IMPORT(PyTypeObject) PyFunction_Type;

View File

@ -1693,13 +1693,39 @@ static struct memberlist instancemethod_memberlist[] = {
/* Dummies that are not handled by getattr() except for __members__ */ /* Dummies that are not handled by getattr() except for __members__ */
{"__doc__", T_INT, 0}, {"__doc__", T_INT, 0},
{"__name__", T_INT, 0}, {"__name__", T_INT, 0},
{"__dict__", T_OBJECT, 0},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyObject * static int
instancemethod_getattr(register PyMethodObject *im, PyObject *name) instancemethod_setattro(register PyMethodObject *im, PyObject *name,
PyObject *v)
{ {
char *sname = PyString_AsString(name); char *sname = PyString_AsString(name);
if (PyEval_GetRestricted() ||
strcmp(sname, "im_func") == 0 ||
strcmp(sname, "im_self") == 0 ||
strcmp(sname, "im_class") == 0)
{
PyErr_Format(PyExc_TypeError, "read-only attribute: %s",
sname);
return -1;
}
if (im->im_self != NULL) {
PyErr_Format(PyExc_TypeError,
"cannot set attributes through bound methods");
return -1;
}
return PyObject_SetAttr(im->im_func, name, v);
}
static PyObject *
instancemethod_getattro(register PyMethodObject *im, PyObject *name)
{
PyObject *rtn;
char *sname = PyString_AsString(name);
if (sname[0] == '_') { if (sname[0] == '_') {
/* Inherit __name__ and __doc__ from the callable object /* Inherit __name__ and __doc__ from the callable object
implementing the method */ implementing the method */
@ -1712,7 +1738,15 @@ instancemethod_getattr(register PyMethodObject *im, PyObject *name)
"instance-method attributes not accessible in restricted mode"); "instance-method attributes not accessible in restricted mode");
return NULL; return NULL;
} }
return PyMember_Get((char *)im, instancemethod_memberlist, sname); 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)) {
PyErr_Clear();
rtn = PyObject_GetAttr(im->im_func, name);
}
return rtn;
} }
static void static void
@ -1832,8 +1866,8 @@ PyTypeObject PyMethod_Type = {
(hashfunc)instancemethod_hash, /*tp_hash*/ (hashfunc)instancemethod_hash, /*tp_hash*/
0, /*tp_call*/ 0, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
(getattrofunc)instancemethod_getattr, /*tp_getattro*/ (getattrofunc)instancemethod_getattro, /*tp_getattro*/
0, /*tp_setattro*/ (setattrofunc)instancemethod_setattro, /*tp_setattro*/
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/
0, /* tp_doc */ 0, /* tp_doc */

View File

@ -30,7 +30,10 @@ PyFunction_New(PyObject *code, PyObject *globals)
doc = Py_None; doc = Py_None;
Py_INCREF(doc); Py_INCREF(doc);
op->func_doc = doc; op->func_doc = doc;
op->func_dict = NULL;
} }
else
return NULL;
PyObject_GC_Init(op); PyObject_GC_Init(op);
return (PyObject *)op; return (PyObject *)op;
} }
@ -102,25 +105,54 @@ static struct memberlist func_memberlist[] = {
}; };
static PyObject * static PyObject *
func_getattr(PyFunctionObject *op, char *name) func_getattro(PyFunctionObject *op, PyObject *name)
{ {
if (name[0] != '_' && PyEval_GetRestricted()) { PyObject *rtn;
char *sname = PyString_AsString(name);
if (sname[0] != '_' && PyEval_GetRestricted()) {
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"function attributes not accessible in restricted mode"); "function attributes not accessible in restricted mode");
return NULL; return NULL;
} }
return PyMember_Get((char *)op, func_memberlist, name);
if (!strcmp(sname, "__dict__") || !strcmp(sname, "func_dict")) {
if (op->func_dict == NULL)
rtn = Py_None;
else
rtn = op->func_dict;
Py_INCREF(rtn);
return rtn;
}
/* no API for PyMember_HasAttr() */
rtn = PyMember_Get((char *)op, func_memberlist, sname);
if (rtn == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
if (op->func_dict != NULL) {
rtn = PyDict_GetItem(op->func_dict, name);
Py_XINCREF(rtn);
}
if (rtn == NULL)
PyErr_SetObject(PyExc_AttributeError, name);
}
return rtn;
} }
static int static int
func_setattr(PyFunctionObject *op, char *name, PyObject *value) func_setattro(PyFunctionObject *op, PyObject *name, PyObject *value)
{ {
int rtn;
char *sname = PyString_AsString(name);
if (PyEval_GetRestricted()) { if (PyEval_GetRestricted()) {
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"function attributes not settable in restricted mode"); "function attributes not settable in restricted mode");
return -1; return -1;
} }
if (strcmp(name, "func_code") == 0) { if (strcmp(sname, "func_code") == 0) {
if (value == NULL || !PyCode_Check(value)) { if (value == NULL || !PyCode_Check(value)) {
PyErr_SetString( PyErr_SetString(
PyExc_TypeError, PyExc_TypeError,
@ -128,7 +160,7 @@ func_setattr(PyFunctionObject *op, char *name, PyObject *value)
return -1; return -1;
} }
} }
else if (strcmp(name, "func_defaults") == 0) { else if (strcmp(sname, "func_defaults") == 0) {
if (value != Py_None && !PyTuple_Check(value)) { if (value != Py_None && !PyTuple_Check(value)) {
PyErr_SetString( PyErr_SetString(
PyExc_TypeError, PyExc_TypeError,
@ -138,7 +170,33 @@ func_setattr(PyFunctionObject *op, char *name, PyObject *value)
if (value == Py_None) if (value == Py_None)
value = NULL; value = NULL;
} }
return PyMember_Set((char *)op, func_memberlist, name, value); else if (!strcmp(sname, "func_dict") || !strcmp(sname, "__dict__")) {
if (value != Py_None && !PyDict_Check(value)) {
PyErr_SetString(
PyExc_TypeError,
"func_dict must be set to a dict object");
return -1;
}
if (value == Py_None)
value = NULL;
Py_XDECREF(op->func_dict);
Py_XINCREF(value);
op->func_dict = value;
return 0;
}
rtn = PyMember_Set((char *)op, func_memberlist, sname, value);
if (rtn < 0 && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
if (op->func_dict == NULL) {
op->func_dict = PyDict_New();
if (op->func_dict == NULL)
return -1;
}
rtn = PyDict_SetItem(op->func_dict, name, value);
}
return rtn;
} }
static void static void
@ -150,6 +208,7 @@ func_dealloc(PyFunctionObject *op)
Py_DECREF(op->func_name); Py_DECREF(op->func_name);
Py_XDECREF(op->func_defaults); Py_XDECREF(op->func_defaults);
Py_XDECREF(op->func_doc); Py_XDECREF(op->func_doc);
Py_XDECREF(op->func_dict);
op = (PyFunctionObject *) PyObject_AS_GC(op); op = (PyFunctionObject *) PyObject_AS_GC(op);
PyObject_DEL(op); PyObject_DEL(op);
} }
@ -227,6 +286,11 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
if (err) if (err)
return err; return err;
} }
if (f->func_dict) {
err = visit(f->func_dict, arg);
if (err)
return err;
}
return 0; return 0;
} }
@ -238,8 +302,8 @@ PyTypeObject PyFunction_Type = {
0, 0,
(destructor)func_dealloc, /*tp_dealloc*/ (destructor)func_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
(getattrfunc)func_getattr, /*tp_getattr*/ 0, /*tp_getattr*/
(setattrfunc)func_setattr, /*tp_setattr*/ 0, /*tp_setattr*/
(cmpfunc)func_compare, /*tp_compare*/ (cmpfunc)func_compare, /*tp_compare*/
(reprfunc)func_repr, /*tp_repr*/ (reprfunc)func_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
@ -248,8 +312,8 @@ PyTypeObject PyFunction_Type = {
(hashfunc)func_hash, /*tp_hash*/ (hashfunc)func_hash, /*tp_hash*/
0, /*tp_call*/ 0, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ (getattrofunc)func_getattro, /*tp_getattro*/
0, /*tp_setattro*/ (setattrofunc)func_setattro, /*tp_setattro*/
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/
0, /* tp_doc */ 0, /* tp_doc */