]> granicus.if.org Git - python/commitdiff
Committing PEP 232, function attribute feature, approved by Guido.
authorBarry Warsaw <barry@python.org>
Mon, 15 Jan 2001 20:40:19 +0000 (20:40 +0000)
committerBarry Warsaw <barry@python.org>
Mon, 15 Jan 2001 20:40:19 +0000 (20:40 +0000)
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.

Include/funcobject.h
Objects/classobject.c
Objects/funcobject.c

index 6ba1e0925e66859bfa06596dcb7505f7eda28c97..35ccf43ed23f152c356eddbd400569654fb234ab 100644 (file)
@@ -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;
index f7fd30c773457daa9786e5f10a37212521ca86d2..4dc72d24c0ef9ef4803fcc84e22adc046d0e4078 100644 (file)
@@ -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 */
index 8b045f4695ea2f6c2903d4144d632971ac6c7a6f..027e97fd3585b43d5fc00ffedc87f4ae076bb9ae 100644 (file)
@@ -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 */