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:
parent
4a420a0a75
commit
d6a9e84c81
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue