Ian Bicking: the old part of his blog

Descriptor Nit

There are some problems with setonce, one being that the resulting classes can't be pickled, because the attributes are unstable. setonce stores its data in the instance in a numbered attribute (e.g., _setonce_attr_15), and that number can change depending on the order in which you construct classes and attributes.

It would be better if setonce named that attribute something like _setonce_classname_attrname, which would be stable. Relatedly, it would be nice if the errors could included the real attribute name.

The problem is that setonce doesn't know where it is being used. It's just an anonymous object, and only when you actually get or set an attribute can it tell what object or class it is bound to, and it still can't tell what attribute it is bound to.

IMHO, this is a serious flaw in descriptors. Most of the time I want to know what class and attribute a descriptor is being assigned to (this comes up a lot in SQLObject particularly).

One possibility is to use a metaclass that goes in and finds all the descriptors with a certain method (say, __setclass__), and call something like descriptor.__setclass__(classObject, attributeName). It would be nice if there was a convention for this; even nicer if it was built into type (the default metaclass). There are various questions: Should __setclass__ return a value? Maybe a new descriptor? Optionally? Does this all happen before or after the class is created? If before, what do we pass in in lieue of classObject? Does this get called only for descriptors that are being added, not for descriptors in superclasses? These aren't particularly hard questions to answer, but if different people will answer them differently unless we have some convention in place.

Created 15 Jul '04
Modified 14 Dec '04


FYI, check out the peak.binding.once.Activator metaclass for an example of something that does what you're trying to do here.

Specifically, it tries to adapt all its new attributes to IActiveDescriptor. If they adapt, it calls the descriptor's 'activateInClass(klass,name)' method, and stores the result in the class dictionary in place of the original descriptor. This happens after the class is created, but before it's returned from __new__.

This approach has worked well for PEAK for over a year now, so if you're trying to get or set a de facto standard, maybe you should follow that one. ;)
# Phillip J. Eby