Edward Diener No Spam wrote: > OK, here is my idea of what such a component model envisages as a list > of items. After this, unless I get some intelligent comments from people > who might be interested in what I envision, or something very similar, I > will be off to investigate it myself rather than do battle with the > horde of people who will just tell me that Python, being a great > language, does not need what I have suggested.
[This quote hacked up by me:] > 1) Component property: This is a glorified attribute with a type that > a) can be specified in a "static" manner, or discovered dynamically, > b) has converters between a string and the actual value > c) has a getter function to retrieve the value if it is readable and a > setter function to set the value if it is writable. > d) be either readable or writable or both. > e) not any Python class attribute since a component has the right > to specify only certain values as manipulatable in a design-time > RAD environment. Whenever you say "glorified attribute", your first thought should be "Python descriptor" (but not your last--it's not a cure-all). They are able to do all of (a, b, c, d, e) which I marked in your text above. For example, here's a descriptor for attributes-you-want-to-persist-in-a-database from my ORM, Dejavu (see http://projects.amor.org/dejavu/browser/trunk/units.py#l290): class UnitProperty(object): """Data descriptor for Unit data which will persist in storage.""" def __init__(self, type=unicode, index=False, hints=None, key=None, default=None): self.type = type self.index = index if hints is None: hints = {} self.hints = hints self.key = key self.default = default def __get__(self, unit, unitclass=None): if unit is None: # When calling on the class instead of an instance... return self else: return unit._properties[self.key] def __set__(self, unit, value): if self.coerce: value = self.coerce(unit, value) oldvalue = unit._properties[self.key] if oldvalue != value: unit._properties[self.key] = value def coerce(self, unit, value): if value is not None and not isinstance(value, self.type): # Try to cast the value to self.type. try: value = self.type(value) except Exception, x: x.args += (value, type(value)) raise return value def __delete__(self, unit): raise AttributeError("Unit Properties may not be deleted.") > a) can be specified in a "static" manner, or discovered dynamically, The "component model" can either scan a class for instances of UnitProperty or keep a registry of them in the class or elsewhere (via a metaclass + add_property functions). > b) has converters between a string and the actual value Note the "coerce" function above. Something similar could be done for serialization (which I can prove in my case because I use UnitProperty to help produce SQL ;) but you could just as easily pickle unit._properties and be done with it. > c) has a getter function to retrieve the value if it is readable and a > setter function to set the value if it is writable. > d) be either readable or writable or both. Descriptors that only have __get__ are read-only; if they have __set__ they are read-write. > e) not any Python class attribute since a component has the right > to specify only certain values as manipulatable in a design-time > RAD environment. Right. Descriptors allow the creator of a class to use "normal" attributes (including functions) which don't participate in the component model. > 2) Component event: This is an type which encapsulates an array, or a > list of callable objects with the same function signature, along with > the functionality to add and remove elements from the array, as well as > cycle through the array calling the callable objects as a particular > event is triggered. A component event is an event source for a > particular event. Component events have to be dicoverable by the Visual > RAD system so that an object's appropriate event handler, an event sink, > can be hooked to the component event itself, an event source, through a > design time interface which propagates the connection at run-time. This can be accomplished by creating a ComponentEvent descriptor whose __get__ returns an object with a __call__ method. Here's a base class for something similar (again, from Dejavu): class UnitAssociation(object): """Non-data descriptor method to retrieve related Units via attributes.""" to_many = None def __init__(self, nearKey, farClass, farKey): # Since the keys will be used as kwarg keys, they must be strings. self.nearKey = str(nearKey) self.farKey = str(farKey) self.nearClass = None self.farClass = farClass def __get__(self, unit, unitclass=None): if unit is None: # When calling on the class instead of an instance... return self else: m = types.MethodType(self.related, unit, unitclass) return m def __delete__(self, unit): raise AttributeError("Unit Associations may not be deleted.") def related(self, unit, expr=None, **kwargs): raise NotImplementedError Subclasses override the "related" method, but a ComponentEvent class could just as easily do: def run(self, *args, **kwargs): for sink in self.sinks: sink(*args, **kwargs) > 3: Component serialization: A component which has its properties and > events set by a visual design-time RAD environment needs to be > serialized at design time and deserialized at run-time. This can be a > default serialization of all component properties and events, or the > component itself can participate in the serilization effort either > wholly or partly. In Dejavu, the UnitProperty class is your "component property" and the Unit class is the component. The Unit class has a copy method: def __copy__(self): newUnit = self.__class__() for key in self.properties: if key in self.identifiers: prop = getattr(self.__class__, key) newUnit._properties[key] = prop.default else: newUnit._properties[key] = self._properties[key] newUnit.sandbox = None return newUnit It wouldn't be hard to replace "newUnit._properties[key] = self._properties[key]" with "dump(self._properties[key])". > 4) Custom property and component editors: A component editor can present > a property editor or an editor for an entire component which the visual > design-time RAD environment can use to allow the programmer end-user of > the component to set or get component property values. Normally a design > time environment will present default property editors for each > component property type, but a component can override this. This is the hard part. I believe Dabo has done some work in this space, but this is where the tight coupling comes in between code and tool, a coupling which Python has traditionally resisted. > 5) Custom type converters: A component should be able to specify a > custom converter for any property to convert, in both directions or > either direction, between the property's string value as seen by a > property editor and the actual value of the component property's type. A ComponentProperty descriptor could include a custom pair of methods to get/set as string. This is often done in web frameworks which need to coerce incoming string values to the correct type. All of which is to say: nobody's done this yet because parts 1, 2, 3 and 5 are trivial to do with descriptors, but actually building a visual RAD environment is too much work. ;) Robert Brewer System Architect Amor Ministries [EMAIL PROTECTED] -- http://mail.python.org/mailman/listinfo/python-list