diff --git a/src/watchdog/observers/fsevents.py b/src/watchdog/observers/fsevents.py index 1226ca4a9..ecfdcee4f 100644 --- a/src/watchdog/observers/fsevents.py +++ b/src/watchdog/observers/fsevents.py @@ -123,6 +123,19 @@ def queue_events(self, timeout): cls = DirDeletedEvent if event.is_directory else FileDeletedEvent 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 + self.stop() + + elif event.is_root_changed: + # This will be set if root or any if its parents is renamed or + # deleted. + # TODO: find out new path and generate DirMovedEvent? + self.queue_event(DirDeletedEvent(self.watch.path)) + self.stop() + i += 1 def run(self): diff --git a/src/watchdog/observers/inotify.py b/src/watchdog/observers/inotify.py index f814fa96c..5cb8c5ddb 100644 --- a/src/watchdog/observers/inotify.py +++ b/src/watchdog/observers/inotify.py @@ -170,6 +170,9 @@ def queue_events(self, timeout, full_events=False): 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_delete_self and src_path == self.watch.path: + self.queue_event(DirDeletedEvent(src_path)) + self.stop() def _decode_path(self, path): """Decode path only if unicode string was passed to this emitter. """ diff --git a/src/watchdog/observers/read_directory_changes.py b/src/watchdog/observers/read_directory_changes.py index 5555630ad..a0e088eb0 100644 --- a/src/watchdog/observers/read_directory_changes.py +++ b/src/watchdog/observers/read_directory_changes.py @@ -22,6 +22,7 @@ from watchdog.events import ( DirCreatedEvent, + DirDeletedEvent, DirMovedEvent, DirModifiedEvent, FileCreatedEvent, @@ -121,6 +122,7 @@ def queue_events(self, timeout): 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() diff --git a/src/watchdog_fsevents.c b/src/watchdog_fsevents.c index a8e8b9893..54ba71732 100644 --- a/src/watchdog_fsevents.c +++ b/src/watchdog_fsevents.c @@ -467,7 +467,7 @@ watchdog_FSEventStreamCreate(StreamCallbackInfo *stream_callback_info_ref, paths, kFSEventStreamEventIdSinceNow, stream_latency, - kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents); + kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagWatchRoot); CFRelease(paths); return stream_ref; } diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 082ec3832..6df98ae45 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -64,7 +64,11 @@ def setup_teardown(tmpdir): yield - emitter.stop() + try: + emitter.stop() + except OSError: + # watch was already stopped, e.g., in `test_delete_self` + pass emitter.join(5) assert not emitter.is_alive() @@ -283,10 +287,12 @@ def test_delete_self(): start_watching(p('dir1')) rm(p('dir1'), True) - if platform.is_darwin(): - event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir1') - assert isinstance(event, FileDeletedEvent) + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1') + assert isinstance(event, DirDeletedEvent) + + emitter.join(timeout=1) + assert not emitter.is_alive() @pytest.mark.skipif(platform.is_windows() or platform.is_bsd(), diff --git a/tests/test_fsevents.py b/tests/test_fsevents.py index bdbf35ad9..88a42e698 100644 --- a/tests/test_fsevents.py +++ b/tests/test_fsevents.py @@ -100,7 +100,9 @@ def on_thread_stop(self): w = observer.schedule(FileSystemEventHandler(), a, recursive=False) rmdir(a) time.sleep(0.1) - observer.unschedule(w) + with pytest.raises(KeyError): + # watch no longer exists! + observer.unschedule(w) def test_watchdog_recursive(): diff --git a/tests/test_inotify_c.py b/tests/test_inotify_c.py index 81f6586ca..a18f140cd 100644 --- a/tests/test_inotify_c.py +++ b/tests/test_inotify_c.py @@ -40,7 +40,11 @@ def watching(path=None, use_full_emitter=False): emitter = Emitter(event_queue, ObservedWatch(path, recursive=True)) emitter.start() yield - emitter.stop() + try: + emitter.stop() + except OSError: + # watch was already stopped, e.g., because root was deleted + pass emitter.join(5)