__all__ = ['metastr'] class metastr(str): def __init__(self, obj): if isinstance(obj, metastr) and hasattr(obj, '_extentsnode_'): self.__dict__['_extentsnode_'] = obj._extentsnode_.copy() def __setattr__(self, name, value): ext = extentsof(self) ext.extents.append((ext.start, ext.length, (name, value))) cleanup(ext.extents) def __getattr__(self, name): ext = extentstmp(self) for i in range(len(ext.extents)-1, -1, -1): start1, length1, (name1, value1) = ext.extents[i] if (name1 == name and start1 <= ext.start and start1+length1 >= ext.start+ext.length): return value1 raise AttributeError(name) def attributes(self): result = {} ext = extentstmp(self) for start1, length1, (name1, value1) in ext.extents: if (start1 <= ext.start and start1+length1 >= ext.start+ext.length): result[name1] = value1 return result def allextents(self): ext = extentstmp(self) return ext.extents def extents(self, name): start_points = {} end_points = {} ext = extentscopy(self) for start1, length1, (name1, value1) in ext.extents: if name1 == name and length1: end1 = start1+length1 if start1 in start_points: end, value = start_points[start1] if end > end1: ... lst.append((length1, value1)) points.setdefault(start1+length1, []) pointlist = points.keys() pointlist.sort() for p in pointlist: start1, length1, value1 = points[0] i = 1 while i < len(points): start2, length2, value2 = points[i] if start1+length1 <= start2: yield (start1, start1+length1, value1) else: def __getitem__(self, index): s = super(metastr, self).__getitem__(index) ext = extentsof(self).indexed(index) return ext.fromstring(s) def __getslice__(self, start, stop): s = super(metastr, self).__getslice__(start, stop) if start < 0: start = 0 if stop > len(self): stop = len(self) ext = extentsof(self).sliced(start, stop) return ext.fromstring(s) def __add__(self, other): s = super(metastr, self).__add__(self, other) ext = extentscopy(self) ext.merge(extentscopy(other), len(self)) return ext.fromstring(s) def __radd__(self, other): s = super(metastr, self).__radd__(self, other) ext = extentscopy(other) ext.merge(extentscopy(self), len(other)) return ext.fromstring(s) def extentsof(s): if isinstance(s, metastr): try: return s.__dict__['_extentsnode_'] except KeyError: ext = ExtentsNode([], 0, len(s)) s.__dict__['_extentsnode_'] = ext return ext else: return ExtentsNode((), 0, len(s)) def extentscopy(s): if isinstance(s, metastr): try: ext = s.__dict__['_extentsnode_'] except KeyError: return ExtentsNode([], 0, len(s)) else: return ext.copy() else: return ExtentsNode((), 0, len(s)) def extentstmp(s): if isinstance(s, metastr): try: return s.__dict__['_extentsnode_'] except KeyError: return ExtentsNode([], 0, len(s)) else: return ExtentsNode((), 0, len(s)) class ExtentsNode(object): def __init__(self, extents, start, length): if length < 0: extents = () start = length = 0 self.extents = extents self.start = start self.length = length def copy(self): ext = [] for (start, length, data) in self.extents: start -= self.start if start < 0: length -= start start = 0 if start+length > self.length: length = self.length - start if length < 0: continue ext.append((start, length, data)) cleanup(ext) return ExtentsNode(ext, 0, self.length) def merge(self, other, offset): for (start, length, data) in other.extents: self.extents.append((start + offset, length, data)) cleanup(self.extents) def fromstring(self, s): assert len(s) == self.length result = metastr(s) result.__dict__['_extentsnode_'] = self return result def indexed(self, index): if index < 0: index += self.length assert 0 <= index < self.length return ExtentsNode(self.extents, self.start+index, 1) def sliced(self, start, stop): return ExtentsNode(self.extents, self.start+start, stop-start) def cleanup(extents): pass if __name__ == '__main__': s = metastr("hello") s[:2].color = 'red' s[3:].color = 'green'