docs: def safe_dir(obj, predicate = None):
      '''
      "Safely" obtain a list of attributes for an
      # object.
  
      Python's dynamic properties are incredibly useful
      but there's a serious lack of good introspection
      tools. Python provides an inspect module which does
      introspection, but at its heart it relies on dir()
      and getattr(), both of which can be overridden by
      classes.
  
      Django of course overrides both of these, which
      means using Python's "proper" introspection class
      will change the object we're trying to display.
      Worse, if you have models with circular references,
      attempting to recursively introspect anything that
      touches the circular reference will trigger an
      infinite recursion loop, crashing the application.
  
      In particular, the trigger for this seems to be
      the use of getattr() inside the inspect.getmembers()
      method. To work around this, we access the object's
      native properties dict.
  
      Unfortunately, using the raw __dict__ will fail
      because it doesn't account for base class properties,
      methods, or anything else fancy. So this function
      attempts to enumerate all of those items. Like the
      inspect.getmembers() call, we accept a predicate
      which will be used to filter the results.
      '''
  
      # safely get all the classes we need to look at
      obj_mro = [ obj ]
      if hasattr(obj.__class__, '__mro__'):
          obj_mro.extend(obj.__class__.__mro__)
      else:
          obj_mro.extend(obj.__class__)
  
      # a set of attributes we will test
      found_attrs = {}
  
      if hasattr(obj, '__dict__'):
          for c in obj_mro:
              # if hasattr(c, '__name__'):
              #     debug_name = c.__name__
              # else:
              #     debug_name = c.__class__.__name__ + ' instance'
              # print 'MRO item:', debug_name
  
              if hasattr(c, '__dict__'):
                  keylist = c.__dict__.keys()
                  for k in keylist:
                      if k not in found_attrs:
                          try:
                              v = obj.__dict__[k]
                          except KeyError: #, AttributeError:
                              # so actually AttributeError should
                              # never happen, but it seems a few
                              # classes will actually report they
                              # have the __dict__ attribute and
                              # then throw an AttributeError when
                              # you try to access it
                              continue
                          if predicate is None or predicate(v):
                              found_attrs[k] = v
                  # print len(keylist), len(c.__dict__.keys())
                  # print 'before:', keylist
                  # print ' after:', c.__dict__.keys()
  
      return sorted(found_attrs.items(), lambda a,b: cmp(a[0],b[0]))