A number of people have played FAST and loose with function and method docstrings, including John Aycock[1], Zope's ORB[2]. Docstrings are handy because they are the one attribute on funcs and methods that are easily writable. But as more people overload the semantics for docstrings, we'll get collisions. I've had a number of discussions with folks about adding attribute dictionaries to functions and methods so that you can essentially add any attribute. Namespaces are one honking great idea -- let's do more of those! Below is a very raw set of patches to add an attribute dictionary to funcs and methods. It's only been minimally tested, but if y'all like the idea, I'll clean it up, sanity check the memory management, and post the changes to patches@python.org. Here's some things you can do: -------------------- snip snip -------------------- Python 1.6a2 (#10, Apr 10 2000, 11:27:59) [GCC 2.8.1] on sunos5 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> def a(): pass ... >>> a.publish = 1 >>> a.publish 1 >>> a.__doc__ >>> a.__doc__ = 'a doc string' >>> a.__doc__ 'a doc string' >>> a.magic_string = a.__doc__ >>> a.magic_string 'a doc string' >>> dir(a) ['__doc__', '__name__', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name', 'magic_string', 'publish'] >>> class F: ... def a(self): pass ... >>> f = F() >>> f.a.publish Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: publish >>> f.a.publish = 1 >>> f.a.publish 1 >>> f.a.__doc__ >>> f.a.__doc__ = 'another doc string' >>> f.a.__doc__ 'another doc string' >>> f.a.magic_string = f.a.__doc__ >>> f.a.magic_string 'another doc string' >>> dir(f.a) ['__dict__', '__doc__', '__name__', 'im_class', 'im_func', 'im_self', 'magic_string', 'publish'] >>> -------------------- snip snip -------------------- -Barry [1] Aycock, "Compiling Little Languages in Python", http://www.foretec.com/python/workshops/1998-11/proceedings/papers/aycock-little/aycock-little.html [2] http://classic.zope.org:8080/Documentation/Reference/ORB P.S. I promised to add a little note about setattr and getattr vs. setattro and getattro. There's very little documentation about the differences, and searching on python.org doesn't seem to turn up anything. The differences are simple. setattr/getattr take a char* argument naming the attribute to change, while setattro/getattro take a PyObject* (hence the trailing `o' -- for Object). This stuff should get documented in the C API, but at least now, it'll turn up in a SIG search. :) -------------------- snip snip -------------------- Index: funcobject.h =================================================================== RCS file: /projects/cvsroot/python/dist/src/Include/funcobject.h,v retrieving revision 2.16 diff -c -r2.16 funcobject.h *** funcobject.h 1998/12/04 18:48:02 2.16 --- funcobject.h 2000/04/07 21:30:40 *************** *** 44,49 **** --- 44,50 ---- PyObject *func_defaults; PyObject *func_doc; PyObject *func_name; + PyObject *func_dict; } PyFunctionObject; extern DL_IMPORT(PyTypeObject) PyFunction_Type; Index: classobject.c =================================================================== RCS file: /projects/cvsroot/python/dist/src/Objects/classobject.c,v retrieving revision 2.84 diff -c -r2.84 classobject.c *** classobject.c 2000/04/10 13:03:19 2.84 --- classobject.c 2000/04/10 15:27:15 *************** *** 1550,1577 **** /* Dummies that are not handled by getattr() except for __members__ */ {"__doc__", T_INT, 0}, {"__name__", T_INT, 0}, {NULL} /* Sentinel */ }; static PyObject * instancemethod_getattr(im, name) register PyMethodObject *im; ! PyObject *name; { ! char *sname = PyString_AsString(name); ! if (sname[0] == '_') { /* Inherit __name__ and __doc__ from the callable object implementing the method */ ! 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"); return NULL; } ! return PyMember_Get((char *)im, instancemethod_memberlist, sname); } static void --- 1550,1608 ---- /* 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_setattr(im, name, v) + register PyMethodObject *im; + char *name; + PyObject *v; + { + int rtn; + + if (PyEval_GetRestricted() || + strcmp(name, "im_func") == 0 || + strcmp(name, "im_self") == 0 || + strcmp(name, "im_class") == 0) + { + PyErr_Format(PyExc_TypeError, "read-only attribute: %s", name); + return -1; + } + return PyObject_SetAttrString(im->im_func, name, v); + } + + static PyObject * instancemethod_getattr(im, name) register PyMethodObject *im; ! char *name; { ! PyObject *rtn; ! ! if (strcmp(name, "__name__") == 0 || ! strcmp(name, "__doc__") == 0) { /* Inherit __name__ and __doc__ from the callable object implementing the method */ ! return PyObject_GetAttrString(im->im_func, name); } if (PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "instance-method attributes not accessible in restricted mode"); return NULL; + } + if (strcmp(name, "__dict__") == 0) + return PyObject_GetAttrString(im->im_func, name); + + rtn = PyMember_Get((char *)im, instancemethod_memberlist, name); + if (rtn == NULL) { + PyErr_Clear(); + rtn = PyObject_GetAttrString(im->im_func, name); + if (rtn == NULL) + PyErr_SetString(PyExc_AttributeError, name); } ! return rtn; } static void *************** *** 1662,1669 **** 0, (destructor)instancemethod_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ ! 0, /*tp_getattr*/ ! 0, /*tp_setattr*/ (cmpfunc)instancemethod_compare, /*tp_compare*/ (reprfunc)instancemethod_repr, /*tp_repr*/ 0, /*tp_as_number*/ --- 1693,1700 ---- 0, (destructor)instancemethod_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ ! (getattrfunc)instancemethod_getattr, /*tp_getattr*/ ! (setattrfunc)instancemethod_setattr, /*tp_setattr*/ (cmpfunc)instancemethod_compare, /*tp_compare*/ (reprfunc)instancemethod_repr, /*tp_repr*/ 0, /*tp_as_number*/ *************** *** 1672,1678 **** (hashfunc)instancemethod_hash, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ ! (getattrofunc)instancemethod_getattr, /*tp_getattro*/ 0, /*tp_setattro*/ }; --- 1703,1709 ---- (hashfunc)instancemethod_hash, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ ! 0, /*tp_getattro*/ 0, /*tp_setattro*/ }; Index: funcobject.c =================================================================== RCS file: /projects/cvsroot/python/dist/src/Objects/funcobject.c,v retrieving revision 2.18 diff -c -r2.18 funcobject.c *** funcobject.c 1998/05/22 00:55:34 2.18 --- funcobject.c 2000/04/07 22:15:33 *************** *** 62,67 **** --- 62,68 ---- doc = Py_None; Py_INCREF(doc); op->func_doc = doc; + op->func_dict = PyDict_New(); } return (PyObject *)op; } *************** *** 133,138 **** --- 134,140 ---- {"__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)}, {"__doc__", T_OBJECT, OFF(func_doc)}, {NULL} /* Sentinel */ }; *************** *** 142,153 **** PyFunctionObject *op; char *name; { if (name[0] != '_' && PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "function attributes not accessible in restricted mode"); return NULL; } ! return PyMember_Get((char *)op, func_memberlist, name); } static int --- 144,167 ---- PyFunctionObject *op; char *name; { + PyObject* rtn; + if (name[0] != '_' && PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "function attributes not accessible in restricted mode"); return NULL; + } + if (strcmp(name, "__dict__") == 0) + return op->func_dict; + + rtn = PyMember_Get((char *)op, func_memberlist, name); + if (rtn == NULL) { + PyErr_Clear(); + rtn = PyDict_GetItemString(op->func_dict, name); + if (rtn == NULL) + PyErr_SetString(PyExc_AttributeError, name); } ! return rtn; } static int *************** *** 156,161 **** --- 170,177 ---- char *name; PyObject *value; { + int rtn; + if (PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "function attributes not settable in restricted mode"); *************** *** 178,185 **** } if (value == Py_None) value = NULL; } ! return PyMember_Set((char *)op, func_memberlist, name, value); } static void --- 194,214 ---- } if (value == Py_None) value = NULL; + } + else if (strcmp(name, "func_dict") == 0) { + if (value == NULL || !PyDict_Check(value)) { + PyErr_SetString( + PyExc_TypeError, + "func_dict must be set to a dict object"); + return -1; + } + } + rtn = PyMember_Set((char *)op, func_memberlist, name, value); + if (rtn < 0) { + PyErr_Clear(); + rtn = PyDict_SetItemString(op->func_dict, name, value); } ! return rtn; } static void *************** *** 191,196 **** --- 220,226 ---- Py_DECREF(op->func_name); Py_XDECREF(op->func_defaults); Py_XDECREF(op->func_doc); + Py_XDECREF(op->func_dict); PyMem_DEL(op); }
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