docs: def cull(self, file_filter=None, attrs=None):
          """ Delete ALL data files and remove torrent from client.
  
              @param file_filter: Optional callable for selecting a subset of all files.
                  The callable gets a file item as described for RtorrentItem._get_files
                  and must return True for items eligible for deletion.
              @param attrs: Optional list of additional attributes to fetch for a filter.
          """
          dry_run = 0 # set to 1 for testing
  
          def remove_with_links(path):
              "Remove a path including any symlink chains leading to it."
              rm_paths = []
              while os.path.islink(path):
                  target = os.readlink(path)
                  rm_paths.append(path)
                  path = target
  
              if os.path.exists(path):
                  rm_paths.append(path)
              else:
                  self._engine.LOG.debug("Real path '%s' doesn't exist,"
                      " but %d symlink(s) leading to it will be deleted..." % (path, len(rm_paths)))
  
              # Remove the link chain, starting at the real path
              # (this prevents losing the chain when there's permission problems)
              for rm_path in reversed(rm_paths):
                  is_dir = os.path.isdir(rm_path) and not os.path.islink(rm_path)
                  self._engine.LOG.debug("Deleting '%s%s'" % (rm_path, '/' if is_dir else ''))
                  if not dry_run:
                      try:
                          (os.rmdir if is_dir else os.remove)(rm_path)
                      except OSError as exc:
                          if exc.errno == errno.ENOENT:
                              # Seems this disappeared somehow inbetween (race condition)
                              self._engine.LOG.info("Path '%s%s' disappeared before it could be deleted"
                                                    % (rm_path, '/' if is_dir else ''))
                          else:
                              raise
  
              return rm_paths
  
          # Assemble doomed files and directories
          files, dirs = set(), set()
          base_path = os.path.expanduser(self.directory)
          item_files = list(self._get_files(attrs=attrs))
  
          if not self.directory:
              raise error.EngineError("Directory for item #%s is empty,"
                  " you might want to add a filter 'directory=!'" % (self._fields["hash"],))
          if not os.path.isabs(base_path):
              raise error.EngineError("Directory '%s' for item #%s is not absolute, which is a bad idea;"
                  " fix your .rtorrent.rc, and use 'directory.default.set = /...'"
                  % (self.directory, self._fields["hash"],))
          if self.fetch("=is_multi_file") and os.path.isdir(self.directory):
              dirs.add(self.directory)
  
          for item_file in item_files:
              if file_filter and not file_filter(item_file):
                  continue
              #print repr(item_file)
              path = os.path.join(base_path, item_file.path)
              files.add(path)
              if '/' in item_file.path:
                  dirs.add(os.path.dirname(path))
  
          # Delete selected files
          if not dry_run:
              self.stop()
          for path in sorted(files):
              ##self._engine.LOG.debug("Deleting file '%s'" % (path,))
              remove_with_links(path)
  
          # Prune empty directories (longer paths first)
          doomed = files | dirs
          for path in sorted(dirs, reverse=True):
              residue = set(os.listdir(path) if os.path.exists(path) else [])
              ignorable = set(i for i in residue
                  if any(fnmatch.fnmatch(i, pat) for pat in config.waif_pattern_list)
                  #or os.path.join(path, i) in doomed
              )
              ##print "---", residue - ignorable
              if residue and residue != ignorable:
                  self._engine.LOG.info("Keeping non-empty directory '%s' with %d %s%s!" % (
                      path, len(residue),
                      "entry" if len(residue) == 1 else "entries",
                      (" (%d ignorable)" % len(ignorable)) if ignorable else "",
                  ))
              else:
                  ##print "---", ignorable
                  for waif in ignorable:# - doomed:
                      waif = os.path.join(path, waif)
                      self._engine.LOG.debug("Deleting waif '%s'" % (waif,))
                      if not dry_run:
                          try:
                              os.remove(waif)
                          except EnvironmentError as exc:
                              self._engine.LOG.warn("Problem deleting waif '%s' (%s)" % (waif, exc))
  
                  ##self._engine.LOG.debug("Deleting empty directory '%s'" % (path,))
                  doomed.update(remove_with_links(path))
  
          # Delete item from engine
          if not dry_run:
              self.delete()