text: from .observable import ObservableBase
  from util.introspect import decormethod
  import sys
  from logging import getLogger; log = getLogger('observablelist')
  
  try:
      sentinel
  except NameError:
      class Sentinel(object):
          def __repr__(self):
              return "<Sentinel (%r backup) %#x>" % (__file__, id(self))
      sentinel = Sentinel()
  
  def list_notify(f):
      def wrapper1(self, *args, **kw):
          old_list = self[:]
          val = f(self, *args,**kw)
          self.notify('list', old_list, self)
          return val
      return wrapper1
  
  class ObservableList(list, ObservableBase):
      "A list you can register observers on."
  
      def __init__(self, *args):
          ObservableBase.__init__(self)
          list.__init__(self, *args)
          self._observable_frozen = False
          self._freezecount = 0
  
      @decormethod
      def notify_wrap(self, func, *args, **kw):
          """
          Methods decorated with notify_wrap make a copy of the list before the
          operation, then notify observers of the change after. The list itself,
          the old list, and the new list are sent as arguments.
          """
          val = func(self, *args,**kw)
          if not self._observable_frozen:
              self.notify('list', None, self)
          return val
  
      def add_list_observer(self, list_changed, child_changed, *attrs, **kwargs):
          ch = kwargs.get('childfunc', lambda child: child)
  
  
          # Sanity check -- this list must be holding Observable children
          if getattr(sys, 'DEV', False):
              for child in [ch(c) for c in self]:
                  if not hasattr(child, 'add_observer'):
                      raise TypeError("This list has non observable children")
  
          class ListWatcher(object):
              def __init__(self, srclist, list_callback, child_callback, *attrs, **kwargs):
                  self.srclist = srclist
                  self.listcopy = set(srclist)
                  srclist.add_observer(self.on_list_changed, **kwargs)
  
                  self.list_callback = list_callback
                  self.child_args = [child_callback] + list(attrs)
                  self.child_kwargs = dict(kwargs)
  
                  # Initial observations.
                  for child in [ch(c) for c in srclist]:
                      child.add_observer(*self.child_args, **self.child_kwargs)
  
              def on_list_changed(self, src, attr, old, new):
  #                if not hasattr(src, 'srclist'):
  #                    print >> sys.stderr, 'WARNING: observablelist.on_list_change not unlinked'
  #                    return
  
                  assert src is self.srclist
  
                  new = set(src)
                  old = self.listcopy
  
                  chargs = self.child_args
                  kwargs = self.child_kwargs
                  for newchild in (new - old):
                      ch(newchild).add_observer(*chargs, **kwargs)
  
                  for oldchild in (old - new):
                      ch(oldchild).remove_observer(*chargs, **kwargs)
  
                  self.listcopy = new
  
              def disconnect(self):
                  chargs, kwargs = self.child_args, self.child_kwargs
                  for child in self.srclist:
                      child.remove_observer(*chargs)
  
                  self.srclist.remove_observer(self.on_list_changed)
                  self.srclist.remove_observer(self.list_callback)
                  self.__dict__.clear()
  
                  self.disconnect = self.disconnect_warning
  
              def disconnect_warning(self):
                  log.critical('calling ListWatcher.disconnect more than once')
  
          # Inform of list structure changes
          if list_changed is not None:
              self.add_observer(list_changed, **kwargs)
  
          # Inform of list children changes
          # call .disconnect on this object when finished.
          return ListWatcher(self, list_changed, child_changed, *attrs, **kwargs)
  
      def remove_list_observer(self, list_changed, child_changed, *attrs):
          if list_changed is not None:
              self.remove_observer(list_changed)
  
      #TODO: uncruft this with some metaclass magic
  
      @notify_wrap
      def __setitem__(self, i, v): return list.__setitem__(self, i, v)
      @notify_wrap
      def __delitem__ (self,key): return list.__delitem__(self, key)
      @notify_wrap
      def __setslice__ (self, i, j, seq): return list.__setslice__(self, i, j, seq)
      @notify_wrap
      def __delslice__(self, i, j): return list.__delslice__(self, i, j)
      @notify_wrap
      def append(self, elem): return list.append(self, elem)
  
      @notify_wrap
      def pop(self, i = sentinel):
          if i is not sentinel:
              return list.pop(self, i)
          else:
              return list.pop(self)
  
      @notify_wrap
      def extend(self, seq): return list.extend(self, seq)
      @notify_wrap
      def insert(self, i, elem): return list.insert(self, i, elem)
      @notify_wrap
      def remove(self, elem): return list.remove(self, elem)
      @notify_wrap
      def reverse(self): return list.reverse(self)
      @notify_wrap
      def sort(self, *a, **k): return list.sort(self, *a, **k)
      @notify_wrap
      def __iadd__(self, item): return list.__iadd__(self, item)
  
  observable_list = ObservableList
  
  if __name__ == '__main__':
      from util.observe.observable import Observable
      class Buddy(Observable):
          def __init__(self, name, status = 'online'):
              Observable.__init__(self)
              self.name = name; self.status = status
  
          def __repr__(self): return '<Buddy %s (%s)>' % (self.name, self.status)
  
      buddies = ObservableList([Buddy(n) for n in 'Bobby Barbera Betty Ben'.split()])
  
      def buddy_list_changed(src, attr, old, new):
          print 'The buddylist changed. New list is %r' % new
  
      def buddy_attr_changed(src, attr, old, new):
          print 'Buddy %r changed %s from %s to %s' % (src, attr, old, new)
  
      buddies.add_list_observer(buddy_list_changed, buddy_attr_changed)
  
      print 'Original list', buddies
      b = buddies[2]
      b.setnotify('status', 'away')
      del buddies[2]
  
      b.setnotify('status', 'online') # should not notify
  