docs: def _attach(self, purrlog, watchdirs=None):
          """Attaches Purr to a purrlog directory, and loads content.
          Returns False if nothing new has been loaded (because directory is the same),
          or True otherwise."""
          purrlog = os.path.abspath(purrlog)
          dprint(1, "attaching to purrlog", purrlog)
          self.logdir = purrlog
          self.indexfile = os.path.join(self.logdir, "index.html")
          self.logtitle = "Unnamed log"
          self.timestamp = self.last_scan_timestamp = time.time()
          self._initIndexDir()
          # reset internal state
          self.ignorelistfile = None
          self.autopounce = False
          self.watched_dirs = []
          self.entries = []
          self._default_dp_props = {}
          self.watchers = {}
          self.temp_watchers = {}
          self.attached = False
          self._watching_state = {}
          # check that we hold a lock on the directory
          self.lockfile = os.path.join(self.logdir, ".purrlock")
          # try to open lock file for r/w
          try:
              self.lockfile_fd = os.open(self.lockfile, os.O_RDWR | os.O_CREAT)
          except:
              raise Purrer.LockFailError("failed to open lock file %s for writing" % self.lockfile)
          # try to acquire lock on the lock file
          try:
              fcntl.lockf(self.lockfile_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
          except:
              other_lock = os.fdopen(self.lockfile_fd, 'r').read()
              self.lockfile_fd = None
              raise Purrer.LockedError(other_lock)
          # got lock, write our ID to the lock file
          global _lockstring
          try:
              self.lockfile_fobj = os.fdopen(self.lockfile_fd, 'w')
              self.lockfile_fobj.write(_lockstring)
              self.lockfile_fobj.flush()
              os.fsync(self.lockfile_fd)
          except:
              raise
          #      raise Purrer.LockFailError("cannot write to lock file %s"%self.lockfile)
          # load log state if log directory already exists
          if os.path.exists(self.logdir):
              _busy = Purr.BusyIndicator()
              if os.path.exists(self.indexfile):
                  try:
                      parser = Purr.Parsers.LogIndexParser()
                      for line in open(self.indexfile):
                          parser.feed(line)
                      self.logtitle = parser.title or self.logtitle
                      self.timestamp = parser.timestamp or self.timestamp
                      dprintf(2, "attached log '%s', timestamp %s\n",
                              self.logtitle, time.strftime("%x %X", time.localtime(self.timestamp)))
                  except:
                      traceback.print_exc()
                      print("Error parsing %s, reverting to defaults" % self.indexfile)
              # load log entries
              entries = []
              for fname in os.listdir(self.logdir):
                  pathname = os.path.join(self.logdir, fname)
                  if Purr.LogEntry.isValidPathname(pathname):
                      try:
                          entry = Purr.LogEntry(load=pathname)
                          dprint(2, "loaded log entry", pathname)
                      except:
                          print("Error loading entry %s, skipping" % fname)
                          traceback.print_exc()
                          continue
                      entries.append(entry)
                  else:
                      dprint(2, fname, "is not a valid Purr entry")
              # sort log entires by timestamp
              entries.sort(lambda a, b: cmp(a.timestamp, b.timestamp))
              self.setLogEntries(entries, save=False)
              # update own timestamp
              if entries:
                  self.timestamp = max(self.timestamp, entries[-1].timestamp)
          # else logfile doesn't exist, create it
          else:
              self._initIndexDir()
          # load configuration if it exists
          # init config file
          self.dirconfig = configparser.RawConfigParser()
          self.dirconfigfile = os.path.join(self.logdir, "dirconfig")
          if os.path.exists(self.dirconfigfile):
              try:
                  self.dirconfig.read(self.dirconfigfile)
              except:
                  print("Error loading config file %s" % self.dirconfigfile)
                  traceback.print_exc()
              # load directory configuration
              for dirname in self.dirconfig.sections():
                  try:
                      watching = self.dirconfig.getint(dirname, "watching")
                  except:
                      watching = Purr.WATCHED
                  dirname = os.path.expanduser(dirname)
                  self.addWatchedDirectory(dirname, watching, save_config=False)
          # start watching the specified directories
          for name in (watchdirs or []):
              self.addWatchedDirectory(name, watching=None)
          # Finally, go through list of ignored files and mark their watchers accordingly.
          # The ignorelist is a list of lines of the form "timestamp filename", giving the timestamp when a
          # file was last "ignored" by the purrlog user.
          self.ignorelistfile = os.path.join(self.logdir, "ignorelist")
          if os.path.exists(self.ignorelistfile):
              # read lines from file, ignore exceptions
              ignores = {}
              try:
                  for line in open(self.ignorelistfile).readlines():
                      timestamp, policy, filename = line.strip().split(" ", 2)
                      # update dictiornary with latest timestamp
                      ignores[filename] = int(timestamp), policy
              except:
                  print("Error reading %s" % self.ignorelistfile)
                  traceback.print_exc()
              # now scan all listed files, and make sure their watchers' mtime is no older than the given
              # last-ignore-timestamp. This ensures that we don't pounce on these files after restarting purr.
              for filename, (timestamp, policy) in ignores.items():
                  watcher = self.watchers.get(filename, None)
                  if watcher:
                      watcher.mtime = max(watcher.mtime, timestamp)
          # init complete
          self.attached = True
          return True