From 39bff247f2acdb1d2a3106985733b5b1f2d29136 Mon Sep 17 00:00:00 2001 From: Corentin Date: Wed, 20 Nov 2024 00:13:50 +0900 Subject: [PATCH] Inotify's symlink follow with recursive option (#1088) * Using symlink follow now working as intended with recursive (used to be shallow only) * Add test for recursive + follow symlink for Inotify observer * Improve test for recursive cases : in case of recursive Inotify directory open event always triggers Co-authored-by: Corentin --- src/watchdog/observers/inotify_c.py | 2 +- tests/test_inotify_buffer.py | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/watchdog/observers/inotify_c.py b/src/watchdog/observers/inotify_c.py index c8c641b6..d52ec3dc 100644 --- a/src/watchdog/observers/inotify_c.py +++ b/src/watchdog/observers/inotify_c.py @@ -408,7 +408,7 @@ def _add_dir_watch(self, path: bytes, mask: int, *, recursive: bool) -> None: raise OSError(errno.ENOTDIR, os.strerror(errno.ENOTDIR), path) self._add_watch(path, mask) if recursive: - for root, dirnames, _ in os.walk(path): + for root, dirnames, _ in os.walk(path, followlinks=self._follow_symlink): for dirname in dirnames: full_path = os.path.join(root, dirname) if not self._follow_symlink and os.path.islink(full_path): diff --git a/tests/test_inotify_buffer.py b/tests/test_inotify_buffer.py index 067b9136..e845f921 100644 --- a/tests/test_inotify_buffer.py +++ b/tests/test_inotify_buffer.py @@ -11,7 +11,8 @@ import random import time -from watchdog.observers.inotify_buffer import InotifyBuffer +from watchdog.observers.inotify_c import InotifyConstants +from watchdog.observers.inotify_buffer import InotifyBuffer, InotifyEvent from .shell import mkdir, mount_tmpfs, mv, rm, symlink, touch, unmount @@ -126,7 +127,29 @@ def test_delete_watched_directory_symlink_followed(p): rm(p("dir", "dir2"), recursive=True) # Wait for the event to be picked up - inotify.read_event() + event = inotify.read_event() + while not isinstance(event, InotifyEvent) or ( + event.mask != (InotifyConstants.IN_DELETE | InotifyConstants.IN_ISDIR)): + event = inotify.read_event() + + # Ensure InotifyBuffer shuts down cleanly without raising an exception + inotify.close() + + +@pytest.mark.timeout(5) +def test_delete_watched_directory_symlink_followed_recursive(p): + mkdir(p("dir"), parents=True) + mkdir(p("dir2", "dir3", "dir4"), parents=True) + symlink(p("dir2"), p("dir", "symdir"), target_is_directory=True) + + inotify = InotifyBuffer(p("dir").encode(), follow_symlink=True, recursive=True) + rm(p("dir2", "dir3", "dir4"), recursive=True) + + # Wait for the event to be picked up + event = inotify.read_event() + while not isinstance(event, InotifyEvent) or ( + event.mask != (InotifyConstants.IN_DELETE | InotifyConstants.IN_ISDIR)): + event = inotify.read_event() # Ensure InotifyBuffer shuts down cleanly without raising an exception inotify.close()