import types class AttributeBase(object): def __get__(self, inst, cls=None): if inst is None: return self try: return inst._attributes[self.name] except KeyError: raise AttributeError("object has no attribute %r" % self.name) def __set__(self, inst, value): actualValue = self.validate(inst, self.name, value) inst._attributes[self.name] = actualValue def validate(self, inst, name, value): raise NotImplementedError("validate must be implemented by a subclass") class NameFinder(type): def __new__(cls, name, bases, classdict): attributeList = [] for k,v in classdict.iteritems(): if isinstance(v, AttributeBase): v.name = k attributeList.append(k) classdict['_attributeList'] = attributeList return type.__new__(cls, name, bases, classdict) class HasAttributes(object): __metaclass__ = NameFinder def __init__(self): self._attributes = {} def getAttributeNames(self): return self._attributeList def getAttributesOfType(self, t, default=None): result = {} for a in self._attributeList: if self.__class__.__dict__[a].__class__ == t: try: value = getattr(self, a) except AttributeError: value = None result[a] = value return result class TypedAttribute(AttributeBase): def validate(self, inst, name, value): if type(value) != self._type: raise TypeError("attribute %s must be of type %s" % (name, self._type)) else: return value # class Option(TypedAttribute): # # _type = types.IntType # # class Param(TypedAttribute): # # _type = types.FloatType # # class String(TypedAttribute): # # _type = types.StringType class TypedSequenceAttribute(AttributeBase): def validate(self, inst, name, value): if type(value) != types.TupleType and type(value) != types.ListType: raise TypeError("attribute %s must be a list or tuple" % (name)) else: for item in value: if type(item) != self._subtype: raise TypeError("attribute %s must be a list or tuple of items with type %s" % (name, self._subtype)) return value # class Instance(AttributeBase): # # def __init__(self, cls): # self.cls = cls # # def validate(self, inst, name, value): # if not isinstance(value, self.cls): # raise TypeError("attribute %s must be an instance of class %s" % (name, self.cls)) # else: # return value # class OptVec(TypedSequenceAttribute): # # _subtype = types.IntType # # class PrmVec(TypedSequenceAttribute): # # _subtype = types.FloatType # # class StrVec(TypedSequenceAttribute): # # _subtype = types.StringType # # # class Bar(HasAttributes): # # a = Option() # # class Foo(HasAttributes): # # a = Option() # b = Param() # c = String() # d = OptVec() # e = PrmVec() # f = StrVec() # h = Instance(Bar)