docs: def get(self, **params):
          """
          Return a list of watched paths that where affected by recent
          changes, following a successful poll() return for the controlling
          file descriptor.
  
          If param "timeout" is greater than 0, the event queue will be read
          multiple times and reads continue until a timeout occurs.
  
          With a timeout active, if param "limit" is greater than 0,
          event reads will stop when the number of changes exceeds the
          limit.  This guarantees that the time the method will block
          will never be greater than timeout*limit seconds.
  
          Note that with a timeout active, multiple changes to a
          single path will only be reported once.
      """
          log = self._getparam('log', self._discard, **params)
  
          self.last_changes = {}
  
          timeout = self._getparam('timeout', 0, **params)
          if not timeout or timeout < 0:
              timeout = 0
          limit = self._getparam('limit', None, **params)
          if not limit or limit < 0:
              limit = None
  
          max_events = limit if limit else 10000
          if self.unprocessed_event:
              log.debug("Will handle unprocessed event")
  
          if self._mode == WF_KQUEUE:
              evagg = {}
              while True:
                  try:
                      evlist = self._kq.control(None, max_events, timeout)
                  except OSError as e:
                      if e.errno == errno.EINTR:
                          break
                      raise e
                  if not evlist:
                      break
  
                  log.debug("kq.control() returned %d event%s", len(evlist), ses(len(evlist)))
                  for ev in evlist:
                      if ev.ident in self.fds_open:
                          path = self.fds_open[ev.ident]
                          if path in evagg:
                              evagg[path].fflags |= ev.fflags
                          else:
                              evagg[path] = ev
                  if limit and len(evagg) >= limit:
                      break
              for path, ev in evagg.items():
                  if ev.fflags & (select.KQ_NOTE_DELETE | select.KQ_NOTE_RENAME):
                      self._disappeared(ev.ident, path, **params)
                  self.last_changes[path] = time.time()
                  log.debug("Change on %r", path)
  
          elif self._mode == WF_INOTIFYX:
              evagg = {}
              while True:
                  try:
                      evlist = pynotifyx.get_events(self._inx_fd, timeout)
                  except IOError as e:
                      if e.errno == errno.EINTR:
                          break
                      raise e
                  if not evlist:
                      break
  
                  log.debug("pynotifyx.get_events() returned %d event%s", len(evlist), ses(len(evlist)))
  
                  for ev in evlist:
                      if ev.wd in self.fds_open:
                          path = self.fds_open[ev.wd]
                          if path in evagg:
                              evagg[path].mask |= ev.mask
                          else:
                              evagg[path] = ev
                      elif ev.mask & pynotifyx.IN_IGNORED:
                          log.debug("skipping IN_IGNORED event on unknown wd %d", ev.wd)
                      else:
                          log.warning("attempt to handle unknown inotify event wd %d", ev.wd)
                  if limit and len(evagg) >= limit:
                      break
              for path, ev in evagg.items():
                  log.debug("Change on %r -- %s", path, ev.get_mask_description())
                  if ev.mask & (pynotifyx.IN_DELETE_SELF | pynotifyx.IN_MOVE_SELF):
                      self._disappeared(ev.wd, path, **params)
                  elif ev.mask & pynotifyx.IN_ATTRIB:
                      file_move_del = False
                      try:
                          s = os.stat(path)
                          if s.st_ino != self._inx_inode[path]:
                              file_move_del = True
                              log.info("'simfs' (used with containers) bug detected -- %r moved", path)
                      except Exception as e:
                          file_move_del = True
                          log.info("'simfs' (used with containers) bug detected -- %r removed", path)
                      if file_move_del:
                          self._disappeared(ev.wd, path, **params)
                  self.last_changes[path] = time.time()
  
          elif self._mode == WF_POLLING:
              #  Consume any pending data from the self-pipe.  Read
              #  until EOF.  The fd is already non-blocking so this
              #  terminates on zero read or any error.
              #
              cnt = 0
              while True:
                  try:
                      data = os.read(self._poll_fd, 1024)
                      if data == '':
                          break
                      cnt += len(data)
                  except OSError as e:
                      if e.errno != errno.EAGAIN:
                          log.warning("Ignoring self-pipe read failure -- %s", e)
                      break
                  except Exception as e:
                      log.warning("Ignoring self-pipe read failure -- %s", e)
                      break
              log.debug("Self-pipe read consumed %d byte%s", cnt, ses(cnt))
              now = time.time()
              for path in self._poll_pending:
                  self.last_changes[path] = self._poll_pending[path]
              self._poll_pending = {}
              for fd in list(self._poll_stat):
                  path = self.fds_open[fd]
                  fstate = self._poll_get_stat(fd, path)
                  if fstate is None:
                      self.last_changes[path] = now
                  elif self._poll_stat[fd] != fstate:
                      self._poll_stat[fd] = fstate
                      self.last_changes[path] = now
                      log.debug("Change on %r", path)
          else:
              raise Exception("Unsupported polling mode " + self.get_mode_name())
          paths = list(self.last_changes)
          paths.sort()
          log.debug("Change was to %d path%s", len(paths), ses(len(paths)))
          return paths