[Python-Dev] Arbitrary attributes on funcs and methods

Greg Stein gstein@lyra.org
Mon, 10 Apr 2000 11:13:08 -0700 (PDT)


On Mon, 10 Apr 2000, Barry Warsaw wrote:
>...
> 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,

+1 on concept, -1 on the patch :-)

>...
> 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. :)

And note that the getattro/setattro is preferred. It is easy to extract
the char* from them; the other direction requires construction of an
object.

>...
> + static int
> + instancemethod_setattr(im, name, v)
> + 	register PyMethodObject *im;
> + 	char *name;
> + 	PyObject *v;

IMO, this should be instancemethod_setattro() and take a PyObject *name.
In the function, you can extract the string for comparison.

>...
> + {
> + 	int rtn;

This variable isn't used.

>...
>   static PyObject *
>   instancemethod_getattr(im, name)
>   	register PyMethodObject *im;
> ! 	char *name;

IMO, this should remain a getattro function. (and fix the name)

In your update, note how many GetAttrString calls there are. The plain
GetAttr is typically faster.

>...
> + 	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);

Why do you mask this second error with the AttributeError? Seems that you
should just leave whatever is there (typically an AttributeError, but
maybe not!).

>...
> --- 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;

This is superfluous. The PyMember_Get will do this.

> + 	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);

Again, with the masking...

>...
> + 	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");

This raises an interesting thought. Why not just require the mapping
protocol?

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/