docs: def rescan(self):
          """Checks files and directories on watchlist for updates, rescans them for new data products.
          If any are found, returns them. Skips those in directories whose watchingState is set to Purr.UNWATCHED.
          """
          if not self.attached:
              return
          dprint(5, "starting rescan")
          newstuff = {};  # this accumulates names of new or changed files. Keys are paths, values are 'quiet' flag.
          # store timestamp of scan
          self.last_scan_timestamp = time.time()
          # go through watched files/directories, check for mtime changes
          for path, watcher in list(self.watchers.items()):
              # get list of new files from watcher
              newfiles = watcher.newFiles()
              # None indicates access error, so drop it from watcher set
              if newfiles is None:
                  if watcher.survive_deletion:
                      dprintf(5, "access error on %s, but will still be watched\n", watcher.path)
                  else:
                      dprintf(2, "access error on %s, will no longer be watched\n", watcher.path)
                      del self.watchers[path]
                  if not watcher.disappeared:
                      self.emit(SIGNAL("disappearedFile"), path)
                      watcher.disappeared = True
                  continue
              dprintf(5, "%s: %d new file(s)\n", watcher.path, len(newfiles))
              # if a file has its own watcher, and is independently reported by a directory watcher, skip the directory's
              # version and let the file's watcher report it. Reason for this is that the file watcher may have a more
              # up-to-date timestamp, so we trust it over the dir watcher.
              newfiles = [p for p in newfiles if p is path or p not in self.watchers]
              # skip files in self._unwatched_paths
              newfiles = [filename for filename in newfiles if
                          self._watching_state.get(os.path.dirname(filename)) > Purr.UNWATCHED]
              # Now go through files and add them to the newstuff dict
              for newfile in newfiles:
                  # if quiet flag is explicitly set on watcher, enforce it
                  # if not pouncing on directory, also add quietly
                  if watcher.quiet or self._watching_state.get(os.path.dirname(newfile)) < Purr.POUNCE:
                      quiet = True
                  # else add quietly if file is not in the quiet patterns
                  else:
                      quiet = matches_patterns(os.path.basename(newfile), self._quiet_patterns)
                  # add file to list of new products. Since a file may be reported by multiple
                  # watchers, make the quiet flag a logical AND of all the quiet flags (i.e. DP will be
                  # marked as quiet only if all watchers report it as quiet).
                  newstuff[newfile] = quiet and newstuff.get(newfile, True)
                  dprintf(4, "%s: new data product, quiet=%d (watcher quiet: %s)\n", newfile, quiet, watcher.quiet)
                  # add a watcher for this file to the temp_watchers list. this is used below
                  # to detect renamed and deleted files
                  self.temp_watchers[newfile] = Purrer.WatchedFile(newfile)
          # now, go through temp_watchers to see if any newly pounced-on files have disappeared
          for path, watcher in list(self.temp_watchers.items()):
              # get list of new files from watcher
              if watcher.newFiles() is None:
                  dprintf(2, "access error on %s, marking as disappeared", watcher.path)
                  del self.temp_watchers[path]
                  self.emit(SIGNAL("disappearedFile"), path)
          # if we have new data products, send them to the main window
          return self.makeDataProducts(iter(newstuff.items()))