--HXjrLbAr5v Content-Type: text/plain; charset=us-ascii Content-Description: message body text Content-Transfer-Encoding: 7bit Here's the second go at adding arbitrary attribute support to function and method objects. Note that this time it's illegal (TypeError) to set an attribute on a bound method object; getting an attribute on a bound method object returns the value on the underlying function object. First the diffs, then the test case and test output. Enjoy, -Barry --HXjrLbAr5v Content-Type: text/plain Content-Description: Diff -u to add arbitrary attrs to funcs and meths Content-Disposition: inline; filename="methdiff.txt" Content-Transfer-Encoding: 7bit Index: Include/funcobject.h =================================================================== RCS file: /projects/cvsroot/python/dist/src/Include/funcobject.h,v retrieving revision 2.16 diff -u -r2.16 funcobject.h --- funcobject.h 1998/12/04 18:48:02 2.16 +++ funcobject.h 2000/04/07 21:30:40 @@ -44,6 +44,7 @@ PyObject *func_defaults; PyObject *func_doc; PyObject *func_name; + PyObject *func_dict; } PyFunctionObject; extern DL_IMPORT(PyTypeObject) PyFunction_Type; Index: Objects/classobject.c =================================================================== RCS file: /projects/cvsroot/python/dist/src/Objects/classobject.c,v retrieving revision 2.84 diff -u -r2.84 classobject.c --- classobject.c 2000/04/10 13:03:19 2.84 +++ classobject.c 2000/04/11 22:05:08 @@ -1550,28 +1550,75 @@ /* Dummies that are not handled by getattr() except for __members__ */ {"__doc__", T_INT, 0}, {"__name__", T_INT, 0}, + {"__dict__", T_INT, 0}, {NULL} /* Sentinel */ }; +static int +instancemethod_setattro(im, name, v) + register PyMethodObject *im; + PyObject *name; + PyObject *v; +{ + char* sname = PyString_AsString(name); + if (sname == NULL) + return -1; + + 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 bound instance-method attribute: %s", + sname); + return -1; + } + return PyObject_SetAttr(im->im_func, name, v); +} + + static PyObject * -instancemethod_getattr(im, name) +instancemethod_getattro(im, name) register PyMethodObject *im; PyObject *name; { - char *sname = PyString_AsString(name); + PyObject *rtn; + char* sname = PyString_AsString(name); + + if (sname == NULL) + return NULL; + if (sname[0] == '_') { /* Inherit __name__ and __doc__ from the callable object - implementing the method */ - if (strcmp(sname, "__name__") == 0 || - strcmp(sname, "__doc__") == 0) + implementing the method. Can't allow access to __dict__ + here because it should not be readable in restricted + execution mode. + */ + if (strcmp(sname, "__name__") == 0 || + strcmp(sname, "__doc__") == 0) { return PyObject_GetAttr(im->im_func, name); + } } if (PyEval_GetRestricted()) { - PyErr_SetString(PyExc_RuntimeError, - "instance-method attributes not accessible in restricted mode"); + PyErr_Format(PyExc_RuntimeError, + "instance-method attributes not accessible in restricted mode: %s", + sname); return NULL; + } + 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_Clear(); + rtn = PyObject_GetAttr(im->im_func, name); } - return PyMember_Get((char *)im, instancemethod_memberlist, sname); + return rtn; } static void @@ -1672,8 +1719,8 @@ (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*/ }; /* Clear out the free list */ Index: Objects/funcobject.c =================================================================== RCS file: /projects/cvsroot/python/dist/src/Objects/funcobject.c,v retrieving revision 2.18 diff -u -r2.18 funcobject.c --- funcobject.c 1998/05/22 00:55:34 2.18 +++ funcobject.c 2000/04/11 22:06:12 @@ -62,6 +62,7 @@ doc = Py_None; Py_INCREF(doc); op->func_doc = doc; + op->func_dict = PyDict_New(); } return (PyObject *)op; } @@ -133,6 +134,8 @@ {"__name__", T_OBJECT, OFF(func_name), READONLY}, {"func_defaults",T_OBJECT, OFF(func_defaults)}, {"func_doc", T_OBJECT, OFF(func_doc)}, + {"func_dict", T_OBJECT, OFF(func_dict)}, + {"__dict__", T_OBJECT, OFF(func_dict)}, {"__doc__", T_OBJECT, OFF(func_doc)}, {NULL} /* Sentinel */ }; @@ -142,12 +145,21 @@ PyFunctionObject *op; char *name; { + PyObject* rtn; + if (name[0] != '_' && PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "function attributes not accessible in restricted mode"); return NULL; + } + rtn = PyMember_Get((char *)op, func_memberlist, name); + if (rtn == NULL) { + PyErr_Clear(); + rtn = PyMapping_GetItemString(op->func_dict, name); + if (rtn == NULL) + PyErr_SetString(PyExc_AttributeError, name); } - return PyMember_Get((char *)op, func_memberlist, name); + return rtn; } static int @@ -156,6 +168,8 @@ char *name; PyObject *value; { + int rtn; + if (PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "function attributes not settable in restricted mode"); @@ -178,8 +192,23 @@ } if (value == Py_None) value = NULL; + } + else if (strcmp(name, "func_dict") == 0 || + strcmp(name, "__dict__") == 0) + { + if (value == NULL || !PyMapping_Check(value)) { + PyErr_SetString( + PyExc_TypeError, + "must set func_dict to a mapping object"); + return -1; + } + } + rtn = PyMember_Set((char *)op, func_memberlist, name, value); + if (rtn < 0) { + PyErr_Clear(); + rtn = PyMapping_SetItemString(op->func_dict, name, value); } - return PyMember_Set((char *)op, func_memberlist, name, value); + return rtn; } static void @@ -191,6 +220,7 @@ Py_DECREF(op->func_name); Py_XDECREF(op->func_defaults); Py_XDECREF(op->func_doc); + Py_XDECREF(op->func_dict); PyMem_DEL(op); } --HXjrLbAr5v Content-Type: text/plain Content-Description: Test of func/meth attrs Content-Disposition: inline; filename="test_funcattrs.py" Content-Transfer-Encoding: 7bit from test_support import verbose class F: def a(self): pass def b(): pass # setting attributes on functions try: b.blah except AttributeError: pass else: print 'did not get expected AttributeError' b.blah = 1 print b.blah == 1 print 'blah' in dir(b) # setting attributes on unbound methods try: F.a.blah except AttributeError: pass else: print 'did not get expected AttributeError' F.a.blah = 1 print F.a.blah == 1 print 'blah' in dir(F.a) # setting attributes on bound methods is illegal f1 = F() try: f1.a.snerp = 1 except TypeError: pass else: print 'did not get expected TypeError' # but accessing attributes on bound methods is fine print f1.a.blah print 'blah' in dir(f1.a) f2 = F() print f1.a.blah == f2.a.blah F.a.wazoo = F f1.a.wazoo is f2.a.wazoo # try setting __dict__ illegally try: F.a.__dict__ = (1, 2, 3) except TypeError: pass else: print 'did not get expected TypeError' F.a.__dict__ = {'one': 111, 'two': 222, 'three': 333} print f1.a.two == 222 from UserDict import UserDict d = UserDict({'four': 444, 'five': 555}) F.a.__dict__ = d try: f2.a.two except AttributeError: pass else: print 'did not get expected AttributeError' print f2.a.four is f1.a.four is F.a.four --HXjrLbAr5v Content-Type: text/plain Content-Description: Output of test of func/meth attrs Content-Disposition: inline; filename="test_funcattrs" Content-Transfer-Encoding: 7bit test_funcattrs 1 1 1 1 1 1 1 1 1 --HXjrLbAr5v--
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4