Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes missing notification for deletion of watched dir on macOS #727

Merged
merged 12 commits into from
Dec 16, 2020
13 changes: 13 additions & 0 deletions src/watchdog/observers/fsevents.py
Expand Up @@ -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):
Expand Down
3 changes: 3 additions & 0 deletions src/watchdog/observers/inotify.py
Expand Up @@ -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. """
Expand Down
2 changes: 2 additions & 0 deletions src/watchdog/observers/read_directory_changes.py
Expand Up @@ -22,6 +22,7 @@

from watchdog.events import (
DirCreatedEvent,
DirDeletedEvent,
DirMovedEvent,
DirModifiedEvent,
FileCreatedEvent,
Expand Down Expand Up @@ -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()


Expand Down
2 changes: 1 addition & 1 deletion src/watchdog_fsevents.c
Expand Up @@ -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;
}
Expand Down
16 changes: 11 additions & 5 deletions tests/test_emitter.py
Expand Up @@ -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()

Expand Down Expand Up @@ -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(),
Expand Down
4 changes: 3 additions & 1 deletion tests/test_fsevents.py
Expand Up @@ -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():
Expand Down
6 changes: 5 additions & 1 deletion tests/test_inotify_c.py
Expand Up @@ -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)


Expand Down