summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBarry Warsaw <barry@python.org>2001-01-15 20:40:19 (GMT)
committerBarry Warsaw <barry@python.org>2001-01-15 20:40:19 (GMT)
commitd6a9e84c8193076e776a1b419148f3043e2f6330 (patch)
treec8fa0c1326e936671bab39e6a6e94138b6bf120f
parent4a420a0a753119bab88d60603a4305b3586982bd (diff)
downloadcpython-d6a9e84c8193076e776a1b419148f3043e2f6330.zip
cpython-d6a9e84c8193076e776a1b419148f3043e2f6330.tar.gz
cpython-d6a9e84c8193076e776a1b419148f3043e2f6330.tar.bz2
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.
-rw-r--r--Include/funcobject.h1
-rw-r--r--Objects/classobject.c42
-rw-r--r--Objects/funcobject.c86
3 files changed, 114 insertions, 15 deletions
diff --git a/Include/funcobject.h b/Include/funcobject.h
index 6ba1e09..35ccf43 100644
--- a/Include/funcobject.h
+++ b/Include/funcobject.h
@@ -14,6 +14,7 @@ typedef struct {
PyObject *func_defaults;
PyObject *func_doc;
PyObject *func_name;
+ PyObject *func_dict;
} PyFunctionObject;
extern DL_IMPORT(PyTypeObject) PyFunction_Type;
diff --git a/Objects/classobject.c b/Objects/classobject.c
index f7fd30c..4dc72d2 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -1693,12 +1693,38 @@ static struct memberlist instancemethod_memberlist[] = {
/* Dummies that are not handled by getattr() except for __members__ */
{"__doc__", T_INT, 0},
{"__name__", T_INT, 0},
+ {"__dict__", T_OBJECT, 0},
{NULL} /* Sentinel */
};
+static int
+instancemethod_setattro(register PyMethodObject *im, PyObject *name,
+ PyObject *v)
+{
+ 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_getattr(register PyMethodObject *im, PyObject *name)
+instancemethod_getattro(register PyMethodObject *im, PyObject *name)
{
+ PyObject *rtn;
char *sname = PyString_AsString(name);
if (sname[0] == '_') {
/* Inherit __name__ and __doc__ from the callable object
@@ -1712,7 +1738,15 @@ instancemethod_getattr(register PyMethodObject *im, PyObject *name)
"instance-method attributes not accessible in restricted mode");
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
@@ -1832,8 +1866,8 @@ PyTypeObject PyMethod_Type = {
(hashfunc)instancemethod_hash, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
- (getattrofunc)instancemethod_getattr, /*tp_getattro*/
- 0, /*tp_setattro*/
+ (getattrofunc)instancemethod_getattro, /*tp_getattro*/
+ (setattrofunc)instancemethod_setattro, /*tp_setattro*/
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/
0, /* tp_doc */
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 8b045f4..027e97f 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -30,7 +30,10 @@ PyFunction_New(PyObject *code, PyObject *globals)
doc = Py_None;
Py_INCREF(doc);
op->func_doc = doc;
+ op->func_dict = NULL;
}
+ else
+ return NULL;
PyObject_GC_Init(op);
return (PyObject *)op;
}
@@ -102,25 +105,54 @@ static struct memberlist func_memberlist[] = {
};
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,
"function attributes not accessible in restricted mode");
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
-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()) {
PyErr_SetString(PyExc_RuntimeError,
"function attributes not settable in restricted mode");
return -1;
}
- if (strcmp(name, "func_code") == 0) {
+ if (strcmp(sname, "func_code") == 0) {
if (value == NULL || !PyCode_Check(value)) {
PyErr_SetString(
PyExc_TypeError,
@@ -128,7 +160,7 @@ func_setattr(PyFunctionObject *op, char *name, PyObject *value)
return -1;
}
}
- else if (strcmp(name, "func_defaults") == 0) {
+ else if (strcmp(sname, "func_defaults") == 0) {
if (value != Py_None && !PyTuple_Check(value)) {
PyErr_SetString(
PyExc_TypeError,
@@ -138,7 +170,33 @@ func_setattr(PyFunctionObject *op, char *name, PyObject *value)
if (value == Py_None)
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
@@ -150,6 +208,7 @@ func_dealloc(PyFunctionObject *op)
Py_DECREF(op->func_name);
Py_XDECREF(op->func_defaults);
Py_XDECREF(op->func_doc);
+ Py_XDECREF(op->func_dict);
op = (PyFunctionObject *) PyObject_AS_GC(op);
PyObject_DEL(op);
}
@@ -227,6 +286,11 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
if (err)
return err;
}
+ if (f->func_dict) {
+ err = visit(f->func_dict, arg);
+ if (err)
+ return err;
+ }
return 0;
}
@@ -238,8 +302,8 @@ PyTypeObject PyFunction_Type = {
0,
(destructor)func_dealloc, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc)func_getattr, /*tp_getattr*/
- (setattrfunc)func_setattr, /*tp_setattr*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
(cmpfunc)func_compare, /*tp_compare*/
(reprfunc)func_repr, /*tp_repr*/
0, /*tp_as_number*/
@@ -248,8 +312,8 @@ PyTypeObject PyFunction_Type = {
(hashfunc)func_hash, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
+ (getattrofunc)func_getattro, /*tp_getattro*/
+ (setattrofunc)func_setattro, /*tp_setattro*/
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/
0, /* tp_doc */