Exemplo n.º 1
0
    def queue_events(self, timeout, full_events=False):
        # If "full_events" is true, then the method will report unmatched move events as separate events
        # This behavior is by default only called by a InotifyFullEmitter
        with self._lock:
            event = self._inotify.read_event()
            if event is None:
                return
            if isinstance(event, tuple):
                move_from, move_to = event
                src_path = self._decode_path(move_from.src_path)
                dest_path = self._decode_path(move_to.src_path)
                cls = DirMovedEvent if move_from.is_directory else FileMovedEvent
                self.queue_event(cls(src_path, dest_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                self.queue_event(DirModifiedEvent(os.path.dirname(dest_path)))
                if move_from.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_moved_events(src_path, dest_path):
                        self.queue_event(sub_event)
                return

            src_path = self._decode_path(event.src_path)
            if event.is_moved_to:
                if full_events:
                    cls = DirMovedEvent if event.is_directory else FileMovedEvent
                    self.queue_event(cls(None, src_path))
                else:
                    cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                    self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                if event.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_created_events(src_path):
                        self.queue_event(sub_event)
            elif event.is_attrib:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_modify:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_delete or (event.is_moved_from and not full_events):
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            elif event.is_moved_from and full_events:
                cls = DirMovedEvent if event.is_directory else FileMovedEvent
                self.queue_event(cls(src_path, None))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            elif event.is_create:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            elif event.is_close_write and not event.is_directory:
                cls = FileClosedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            # elif event.is_close_nowrite and not event.is_directory:
            #     cls = FileClosedEvent
            #     self.queue_event(cls(src_path))
            elif event.is_delete_self and src_path == self.watch.path:
                self.queue_event(DirDeletedEvent(src_path))
                self.stop()
Exemplo n.º 2
0
    def _gen_renamed_events(self,
                            src_path,
                            is_directory,
                            ref_snapshot,
                            new_snapshot):
        """
        Compares information from two directory snapshots (one taken before
        the rename operation and another taken right after) to determine the
        destination path of the file system object renamed, and yields
        the appropriate events to be queued.
        """
        try:
            f_inode = ref_snapshot.inode(src_path)
        except KeyError:
            # Probably caught a temporary file/directory that was renamed
            # and deleted. Fires a sequence of created and deleted events
            # for the path.
            if is_directory:
                yield DirCreatedEvent(src_path)
                yield DirDeletedEvent(src_path)
            else:
                yield FileCreatedEvent(src_path)
                yield FileDeletedEvent(src_path)
                # We don't process any further and bail out assuming
            # the event represents deletion/creation instead of movement.
            return

        dest_path = new_snapshot.path(f_inode)
        if dest_path is not None:
            dest_path = absolute_path(dest_path)
            if is_directory:
                event = DirMovedEvent(src_path, dest_path)
                yield event
            else:
                yield FileMovedEvent(src_path, dest_path)
            yield self._parent_dir_modified(src_path)
            yield self._parent_dir_modified(dest_path)
            if is_directory:
                # TODO: Do we need to fire moved events for the items
                # inside the directory tree? Does kqueue does this
                # all by itself? Check this and then enable this code
                # only if it doesn't already.
                # A: It doesn't. So I've enabled this block.
                if self.watch.is_recursive:
                    for sub_event in generate_sub_moved_events(src_path, dest_path):
                        yield sub_event
        else:
            # If the new snapshot does not have an inode for the
            # old path, we haven't found the new name. Therefore,
            # we mark it as deleted and remove unregister the path.
            if is_directory:
                yield DirDeletedEvent(src_path)
            else:
                yield FileDeletedEvent(src_path)
            yield self._parent_dir_modified(src_path)
Exemplo n.º 3
0
    def queue_events(self, timeout):
        winapi_events = self._read_events()
        with self._lock:
            last_renamed_src_path = ""
            for winapi_event in winapi_events:
                src_path = os.path.join(self.watch.path, winapi_event.src_path)

                if winapi_event.is_renamed_old:
                    last_renamed_src_path = src_path
                elif winapi_event.is_renamed_new:
                    dest_path = src_path
                    src_path = last_renamed_src_path
                    if os.path.isdir(dest_path):
                        event = DirMovedEvent(src_path, dest_path)
                        if self.watch.is_recursive:
                            # HACK: We introduce a forced delay before
                            # traversing the moved directory. This will read
                            # only file movement that finishes within this
                            # delay time.
                            time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                            # The following block of code may not
                            # obtain moved events for the entire tree if
                            # the I/O is not completed within the above
                            # delay time. So, it's not guaranteed to work.
                            # TODO: Come up with a better solution, possibly
                            # a way to wait for I/O to complete before
                            # queuing events.
                            for sub_moved_event in generate_sub_moved_events(
                                    src_path, dest_path):
                                self.queue_event(sub_moved_event)
                        self.queue_event(event)
                    else:
                        self.queue_event(FileMovedEvent(src_path, dest_path))
                elif winapi_event.is_modified:
                    cls = DirModifiedEvent if os.path.isdir(
                        src_path) else FileModifiedEvent
                    self.queue_event(cls(src_path))
                elif winapi_event.is_added:
                    isdir = os.path.isdir(src_path)
                    cls = DirCreatedEvent if isdir else FileCreatedEvent
                    self.queue_event(cls(src_path))
                    if isdir and self.watch.is_recursive:
                        # If a directory is moved from outside the watched folder to inside it
                        # we only get a created directory event out of it, not any events for its children
                        # so use the same hack as for file moves to get the child events
                        time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                        sub_events = generate_sub_created_events(src_path)
                        for sub_created_event in sub_events:
                            self.queue_event(sub_created_event)
                elif winapi_event.is_removed:
                    self.queue_event(FileDeletedEvent(src_path))
                elif winapi_event.is_removed_self:
                    self.queue_event(DirDeletedEvent(self.watch.path))
                    self.stop()
Exemplo n.º 4
0
    def queue_events(self, timeout, full_events=False):
        #If "full_events" is true, then the method will report unmatched move events as seperate events
        #This behavior is by default only called by a InotifyFullEmitter
        with self._lock:
            event = self._inotify.read_event()
            if event is None:
                return
            if isinstance(event, tuple):
                move_from, move_to = event
                src_path = self._decode_path(move_from.src_path)
                dest_path = self._decode_path(move_to.src_path)
                cls = DirMovedEvent if move_from.is_directory else FileMovedEvent
                self.queue_event(cls(src_path, dest_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                self.queue_event(DirModifiedEvent(os.path.dirname(dest_path)))
                if move_from.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_moved_events(src_path, dest_path):
                        self.queue_event(sub_event)
                return

            src_path = self._decode_path(event.src_path)
            if event.is_moved_to:
                if (full_events):
                    cls = DirMovedEvent if event.is_directory else FileMovedEvent
                    self.queue_event(cls(None, src_path))
                else:
                    cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                    self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                if event.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_created_events(src_path):
                        self.queue_event(sub_event)
            elif event.is_attrib:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_modify:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_delete_self:
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                self.queue_event(cls(src_path))
            elif event.is_delete or (event.is_moved_from and not full_events):
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            elif event.is_moved_from and full_events:
                cls = DireMovedEvent if event.is_directory else FileMovedEvent
                self.queue_event(cls(src_path, None))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            elif event.is_create:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
 def queue_events(self, timeout):
     winapi_events = read_events(self._handle, self.watch.is_recursive)
     with self._lock:
         last_renamed_src_path = ""
         for winapi_event in winapi_events:
             src_path = os.path.join(self.watch.path, winapi_event.src_path)
             
             if winapi_event.is_renamed_old:
                 last_renamed_src_path = src_path
             elif winapi_event.is_renamed_new:
                 dest_path = src_path
                 src_path = last_renamed_src_path
                 if os.path.isdir(dest_path):
                     event = DirMovedEvent(src_path, dest_path)
                     if self.watch.is_recursive:
                         # HACK: We introduce a forced delay before
                         # traversing the moved directory. This will read
                         # only file movement that finishes within this
                         # delay time.
                         time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                         # The following block of code may not
                         # obtain moved events for the entire tree if
                         # the I/O is not completed within the above
                         # delay time. So, it's not guaranteed to work.
                         # TODO: Come up with a better solution, possibly
                         # a way to wait for I/O to complete before
                         # queuing events.
                         for sub_moved_event in generate_sub_moved_events(src_path, dest_path):
                             self.queue_event(sub_moved_event)
                     self.queue_event(event)
                 else:
                     self.queue_event(FileMovedEvent(src_path, dest_path))
             elif winapi_event.is_modified:
                 cls = DirModifiedEvent if os.path.isdir(src_path) else FileModifiedEvent
                 self.queue_event(cls(src_path))
             elif winapi_event.is_added:
                 isdir = os.path.isdir(src_path)
                 cls = DirCreatedEvent if isdir else FileCreatedEvent
                 self.queue_event(cls(src_path))
                 if isdir:
                     # If a directory is moved from outside the watched folder to inside it
                     # we only get a created directory event out of it, not any events for its children
                     # so use the same hack as for file moves to get the child events
                     time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                     sub_events = generate_sub_created_events(src_path)
                     for sub_created_event in sub_events:
                         self.queue_event(sub_created_event)
             elif winapi_event.is_removed:
                 self.queue_event(FileDeletedEvent(src_path))
Exemplo n.º 6
0
    def queue_events(self, timeout):
        with self._lock:
            event = self._inotify.read_event()

            if event is STOP_EVENT:
                return

            if isinstance(event, tuple):
                move_from, move_to = event
                src_path = self._decode_path(move_from.src_path)
                dest_path = self._decode_path(move_to.src_path)
                cls = DirMovedEvent if move_from.is_directory else FileMovedEvent
                self.queue_event(cls(src_path, dest_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                self.queue_event(DirModifiedEvent(os.path.dirname(dest_path)))
                if move_from.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_moved_events(src_path, dest_path):
                        self.queue_event(sub_event)
                return

            src_path = self._decode_path(event.src_path)
            if event.is_moved_to:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                if event.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_created_events(src_path):
                        self.queue_event(sub_event)
            elif event.is_attrib:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_modify:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_delete_self:
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                self.queue_event(cls(src_path))
            elif event.is_delete or event.is_moved_from:
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            elif event.is_create:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
Exemplo n.º 7
0
    def queue_events(self, timeout):
        with self._lock:
            event = self._inotify.read_event()
            if event is None:
                return
            if isinstance(event, tuple):
                move_from, move_to = event
                src_path = self._decode_path(move_from.src_path)
                dest_path = self._decode_path(move_to.src_path)
                cls = DirMovedEvent if move_from.is_directory else FileMovedEvent
                self.queue_event(cls(src_path, dest_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                self.queue_event(DirModifiedEvent(os.path.dirname(dest_path)))
                if move_from.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_moved_events(
                            src_path, dest_path):
                        self.queue_event(sub_event)
                return

            src_path = self._decode_path(event.src_path)
            if event.is_moved_to:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                if event.is_directory and self.watch.is_recursive:
                    for sub_event in generate_sub_created_events(src_path):
                        self.queue_event(sub_event)
            elif event.is_attrib:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_modify:
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))
            elif event.is_delete_self:
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                self.queue_event(cls(src_path))
            elif event.is_delete or event.is_moved_from:
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
            elif event.is_create:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                self.queue_event(cls(src_path))
                self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
Exemplo n.º 8
0
    def queue_events(self, timeout):
        winapi_events = read_events(self._handle, self.watch.is_recursive)
        with self._lock:
            last_renamed_src_path = ''
            for winapi_event in winapi_events:
                src_path = os.path.join(self.watch.path, winapi_event.src_path)
                if winapi_event.is_renamed_old:
                    last_renamed_src_path = src_path
                elif winapi_event.is_renamed_new:
                    dest_path = src_path
                    src_path = last_renamed_src_path
                    if os.path.isdir(dest_path):
                        event = DirMovedEvent(src_path, dest_path)
                        if self.watch.is_recursive:
                            time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                            for sub_moved_event in generate_sub_moved_events(
                                    src_path, dest_path):
                                self.queue_event(sub_moved_event)

                        self.queue_event(event)
                    else:
                        self.queue_event(FileMovedEvent(src_path, dest_path))
                elif winapi_event.is_modified:
                    cls = DirModifiedEvent if os.path.isdir(
                        src_path) else FileModifiedEvent
                    self.queue_event(cls(src_path))
                elif winapi_event.is_added:
                    isdir = os.path.isdir(src_path)
                    cls = DirCreatedEvent if isdir else FileCreatedEvent
                    self.queue_event(cls(src_path))
                    if isdir:
                        time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                        sub_events = generate_sub_created_events(src_path)
                        for sub_created_event in sub_events:
                            self.queue_event(sub_created_event)

                elif winapi_event.is_removed:
                    self.queue_event(FileDeletedEvent(src_path))
    def queue_events(self, timeout):
        winapi_events = read_events(self._handle, self.watch.is_recursive)
        with self._lock:
            last_renamed_src_path = ''
            for winapi_event in winapi_events:
                src_path = os.path.join(self.watch.path, winapi_event.src_path)
                if winapi_event.is_renamed_old:
                    last_renamed_src_path = src_path
                elif winapi_event.is_renamed_new:
                    dest_path = src_path
                    src_path = last_renamed_src_path
                    if os.path.isdir(dest_path):
                        event = DirMovedEvent(src_path, dest_path)
                        if self.watch.is_recursive:
                            time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                            for sub_moved_event in generate_sub_moved_events(src_path, dest_path):
                                self.queue_event(sub_moved_event)

                        self.queue_event(event)
                    else:
                        self.queue_event(FileMovedEvent(src_path, dest_path))
                elif winapi_event.is_modified:
                    cls = DirModifiedEvent if os.path.isdir(src_path) else FileModifiedEvent
                    self.queue_event(cls(src_path))
                elif winapi_event.is_added:
                    isdir = os.path.isdir(src_path)
                    cls = DirCreatedEvent if isdir else FileCreatedEvent
                    self.queue_event(cls(src_path))
                    if isdir:
                        time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                        sub_events = generate_sub_created_events(src_path)
                        for sub_created_event in sub_events:
                            self.queue_event(sub_created_event)

                elif winapi_event.is_removed:
                    self.queue_event(FileDeletedEvent(src_path))
Exemplo n.º 10
0
    def queue_events(self, timeout, events):

        if logger.getEffectiveLevel() <= logging.DEBUG:
            for event in events:
                flags = ", ".join(attr for attr in dir(event) if getattr(event, attr) is True)
                logger.debug(f"{event}: {flags}")

        while events:
            event = events.pop(0)
            src_path = self._encode_path(event.path)

            if event.is_renamed:
                dest_event = next(iter(e for e in events if e.is_renamed and e.inode == event.inode), None)
                if dest_event:
                    # item was moved within the watched folder
                    events.remove(dest_event)
                    logger.debug("Destination event for rename is %s", dest_event)
                    cls = DirMovedEvent if event.is_directory else FileMovedEvent
                    dst_path = self._encode_path(dest_event.path)
                    self.queue_event(cls(src_path, dst_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                    self.queue_event(DirModifiedEvent(os.path.dirname(dst_path)))
                    for sub_event in generate_sub_moved_events(src_path, dst_path):
                        logger.debug("Generated sub event: %s", sub_event)
                        self.queue_event(sub_event)
                elif os.path.exists(event.path):
                    # item was moved into the watched folder
                    cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                    for sub_event in generate_sub_created_events(src_path):
                        self.queue_event(sub_event)
                else:
                    # item was moved out of the watched folder
                    cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))

            if event.is_created:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                if not event.is_coalesced or (
                    event.is_coalesced and not event.is_renamed and not event.is_modified and not
                    event.is_inode_meta_mod and not event.is_xattr_mod
                ):
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))

            if event.is_modified and not event.is_coalesced and os.path.exists(src_path):
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))

            if event.is_inode_meta_mod or event.is_xattr_mod:
                if os.path.exists(src_path) and not event.is_coalesced:
                    # NB: in the scenario of touch(file) -> rm(file) we can trigger this twice
                    cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                    self.queue_event(cls(src_path))

            if event.is_removed:
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                if not event.is_coalesced or (event.is_coalesced and not os.path.exists(event.path)):
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))

                    if src_path == self.watch.path:
                        # this should not really occur, instead we expect
                        # is_root_changed to be set
                        logger.debug("Stopping because root path was removed")
                        self.stop()

            if event.is_root_changed:
                # This will be set if root or any of its parents is renamed or
                # deleted.
                # TODO: find out new path and generate DirMovedEvent?
                self.queue_event(DirDeletedEvent(self.watch.path))
                logger.debug("Stopping because root path was changed")
                self.stop()
Exemplo n.º 11
0
    def queue_events(self, timeout, events):

        if logger.getEffectiveLevel() <= logging.DEBUG:
            for event in events:
                flags = ", ".join(attr for attr in dir(event)
                                  if getattr(event, attr) is True)
                logger.debug(f"{event}: {flags}")

        if time.monotonic() - self._start_time > 60:
            # Event history is no longer needed, let's free some memory.
            self._starting_state = None

        while events:
            event = events.pop(0)

            src_path = self._encode_path(event.path)
            src_dirname = os.path.dirname(src_path)

            try:
                stat = os.stat(src_path)
            except OSError:
                stat = None

            exists = stat and stat.st_ino == event.inode

            # FSevents may coalesce multiple events for the same item + path into a
            # single event. However, events are never coalesced for different items at
            # the same path or for the same item at different paths. Therefore, the
            # event chains "removed -> created" and "created -> renamed -> removed" will
            # never emit a single native event and a deleted event *always* means that
            # the item no longer existed at the end of the event chain.

            # Some events will have a spurious `is_created` flag set, coalesced from an
            # already emitted and processed CreatedEvent. To filter those, we keep track
            # of all inodes which we know to be already created. This is safer than
            # keeping track of paths since paths are more likely to be reused than
            # inodes.

            # Likewise, some events will have a spurious `is_modified`,
            # `is_inode_meta_mod` or `is_xattr_mod` flag set. We currently do not
            # suppress those but could do so if the item still exists by caching the
            # stat result and verifying that it did change.

            if event.is_created and event.is_removed:

                # Events will only be coalesced for the same item / inode.
                # The sequence deleted -> created therefore cannot occur.
                # Any combination with renamed cannot occur either.

                if not self._is_historic_created_event(event):
                    self._queue_created_event(event, src_path, src_dirname)

                self._fs_view.add(event.inode)

                if event.is_modified or self._is_meta_mod(event):
                    self._queue_modified_event(event, src_path, src_dirname)

                self._queue_deleted_event(event, src_path, src_dirname)
                self._fs_view.discard(event.inode)

            else:

                if event.is_created and not self._is_historic_created_event(
                        event):
                    self._queue_created_event(event, src_path, src_dirname)

                self._fs_view.add(event.inode)

                if event.is_modified or self._is_meta_mod(event):
                    self._queue_modified_event(event, src_path, src_dirname)

                if event.is_renamed:

                    # Check if we have a corresponding destination event in the watched path.
                    dst_event = next(
                        iter(e for e in events
                             if e.is_renamed and e.inode == event.inode), None)

                    if dst_event:
                        # Item was moved within the watched folder.
                        logger.debug("Destination event for rename is %s",
                                     dst_event)

                        dst_path = self._encode_path(dst_event.path)
                        dst_dirname = os.path.dirname(dst_path)

                        self._queue_renamed_event(event, src_path, dst_path,
                                                  src_dirname, dst_dirname)
                        self._fs_view.add(event.inode)

                        for sub_event in generate_sub_moved_events(
                                src_path, dst_path):
                            self.queue_event(sub_event)

                        # Process any coalesced flags for the dst_event.

                        events.remove(dst_event)

                        if dst_event.is_modified or self._is_meta_mod(
                                dst_event):
                            self._queue_modified_event(dst_event, dst_path,
                                                       dst_dirname)

                        if dst_event.is_removed:
                            self._queue_deleted_event(dst_event, dst_path,
                                                      dst_dirname)
                            self._fs_view.discard(dst_event.inode)

                    elif exists:
                        # This is the destination event, item was moved into the watched
                        # folder.
                        self._queue_created_event(event, src_path, src_dirname)
                        self._fs_view.add(event.inode)

                        for sub_event in generate_sub_created_events(src_path):
                            self.queue_event(sub_event)

                    else:
                        # This is the source event, item was moved out of the watched
                        # folder.
                        self._queue_deleted_event(event, src_path, src_dirname)
                        self._fs_view.discard(event.inode)

                        # Skip further coalesced processing.
                        continue

                if event.is_removed:
                    # Won't occur together with renamed.
                    self._queue_deleted_event(event, src_path, src_dirname)
                    self._fs_view.discard(event.inode)

            if event.is_root_changed:
                # This will be set if root or any of its parents is renamed or deleted.
                # TODO: find out new path and generate DirMovedEvent?
                self.queue_event(DirDeletedEvent(self.watch.path))
                logger.debug("Stopping because root path was changed")
                self.stop()

                self._fs_view.clear()
Exemplo n.º 12
0
    def queue_events(self, timeout, events):
        i = 0
        while i < len(events):
            event = events[i]
            logger.info(event)
            src_path = self._encode_path(event.path)

            if event.is_renamed:
                # Internal moves appears to always be consecutive in the same
                # buffer and have IDs differ by exactly one (while others
                # don't) making it possible to pair up the two events coming
                # from a singe move operation. (None of this is documented!)
                # Otherwise, guess whether file was moved in or out.
                # TODO: handle id wrapping
                if (i + 1 < len(events) and events[i + 1].is_renamed
                        and events[i + 1].event_id == event.event_id + 1):
                    logger.info("Next event for rename is %s", events[i + 1])
                    cls = DirMovedEvent if event.is_directory else FileMovedEvent
                    dst_path = self._encode_path(events[i + 1].path)
                    self.queue_event(cls(src_path, dst_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                    self.queue_event(DirModifiedEvent(os.path.dirname(dst_path)))
                    for sub_event in generate_sub_moved_events(src_path, dst_path):
                        logger.info("Generated sub event: %s", sub_event)
                        self.queue_event(sub_event)
                    i += 1
                elif os.path.exists(event.path):
                    cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
                    for sub_event in generate_sub_created_events(src_path):
                        self.queue_event(sub_event)
                else:
                    cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))

            if event.is_created:
                cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
                if not event.is_coalesced or (
                    event.is_coalesced and not event.is_renamed and not event.is_modified and not
                    event.is_inode_meta_mod and not event.is_xattr_mod
                ):
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))

            if event.is_modified and not event.is_coalesced and os.path.exists(src_path):
                cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                self.queue_event(cls(src_path))

            if event.is_inode_meta_mod or event.is_xattr_mod:
                if os.path.exists(src_path) and not event.is_coalesced:
                    # NB: in the scenario of touch(file) -> rm(file) we can trigger this twice
                    cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
                    self.queue_event(cls(src_path))

            if event.is_removed:
                cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                if not event.is_coalesced or (event.is_coalesced and not os.path.exists(event.path)):
                    self.queue_event(cls(src_path))
                    self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))

                    if src_path == self.watch.path:
                        # this should not really occur, instead we expect
                        # is_root_changed to be set
                        logger.info("Stopping because root path was removed")
                        self.stop()

            if event.is_root_changed:
                # This will be set if root or any of its parents is renamed or
                # deleted.
                # TODO: find out new path and generate DirMovedEvent?
                self.queue_event(DirDeletedEvent(self.watch.path))
                logger.info("Stopping because root path was changed")
                self.stop()

            i += 1
Exemplo n.º 13
0
    def queue_events(self, timeout):
        try:
            logger.debug('Reading events')
            winapi_events = read_events(self._handle, self.watch.is_recursive)
        except WatchedDirDeleted:
            logger.warning("Watched dir was deleted")
            self.queue_event(FileDeletedEvent(self.watch.path))
            self.stop()
        except OSError as err:
            #  The specified network name is no longer available.
            if err.winerror == 64 or err.winerror == 59:
                self.queue_event(FileSystemDisconnected(self.watch.path))
                time.sleep(0.2)
                return
            raise

        with self._lock:
            last_renamed_src_path = ""
            logger.debug('Processing events')
            for winapi_event in winapi_events:
                logger.debug('Raw event from winapi: %s', winapi_event)
                src_path = os.path.join(self.watch.path, winapi_event.src_path)

                if winapi_event.is_renamed_old:
                    last_renamed_src_path = src_path
                elif winapi_event.is_renamed_new:
                    dest_path = src_path
                    src_path = last_renamed_src_path
                    if os.path.isdir(dest_path):
                        event = DirMovedEvent(src_path, dest_path)
                        if self.watch.is_recursive:
                            # HACK: We introduce a forced delay before
                            # traversing the moved directory. This will read
                            # only file movement that finishes within this
                            # delay time.
                            time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                            # The following block of code may not
                            # obtain moved events for the entire tree if
                            # the I/O is not completed within the above
                            # delay time. So, it's not guaranteed to work.
                            # TODO: Come up with a better solution, possibly
                            # a way to wait for I/O to complete before
                            # queuing events.
                            for sub_moved_event in generate_sub_moved_events(src_path, dest_path):
                                self.queue_event(sub_moved_event)
                        self.queue_event(event)
                    else:
                        self.queue_event(FileMovedEvent(src_path, dest_path))
                elif winapi_event.is_modified:
                    cls = DirModifiedEvent if os.path.isdir(src_path) else FileModifiedEvent
                    self.queue_event(cls(src_path))
                elif winapi_event.is_added:
                    isdir = os.path.isdir(src_path)
                    cls = DirCreatedEvent if isdir else FileCreatedEvent
                    self.queue_event(cls(src_path))
                    if isdir:
                        # If a directory is moved from outside the watched folder to inside it
                        # we only get a created directory event out of it, not any events for its children
                        # so use the same hack as for file moves to get the child events
                        time.sleep(WATCHDOG_TRAVERSE_MOVED_DIR_DELAY)
                        sub_events = generate_sub_created_events(src_path)
                        for sub_created_event in sub_events:
                            self.queue_event(sub_created_event)
                elif winapi_event.is_removed:
                    self.queue_event(FileDeletedEvent(src_path))