libc = ctypes.CDLL(ctypes.util.findlibrary("c"), useerrno=True)
The implementation uses raw Linux inotify via ctypes (zero external dependencies). Key design:
Recursive watching – on start(), os.walk() scans the root tree and adds an inotify watch for every directory. When IN_CREATE | IN_ISDIR fires, a watch is added for the new directory and any already-populated subdirectories (handles os.makedirs race).
Event detection – the event loop reads raw struct inotify_event records from the inotify fd and maps them to four user-facing types: created, modified, deleted, moved. MOVED_FROM/MOVED_TO pairs are matched by cookie to produce a single moved event.
Debouncing – a _Debouncer class collects events per-path. On each new event it resets a timer; when the timer fires (default 300ms quiet), the final event type is emitted. Merge rules: deleted always wins; created takes precedence over modified.
Thread safety – all debouncer state is protected by a lock. The inotify event loop runs in a daemon thread. stop() signals the loop, drains remaining events, and closes the fd.
import os, time, ctypes, threading, struct, errno
# ctypes bindings for inotify_init, inotify_add_watch, inotify_rm_watch, close
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
_clib.inotify_init.restype = ctypes.c_int
# ... (see solution.py for full source)
class RecursiveDirWatcher:
def __init__(self, root_dir, callback, debounce_interval=0.3): ...
def start(self): ...
def stop(self): ...
Tested with 9 test cases covering all event types and edge cases: | Test | Result | |------|--------| | `test_basic_file_ops` – create, modify, delete a file | ✅ detected all three | | `test_subdirectory_creation` – mkdir + file inside | ✅ file in new subdir detected | | `test_move` – rename a file | ✅ `moved` event with src→dst | | `test_debounce` – 4 rapid writes | ✅ 1–2 events (debounced) | | `test_nested_delete` – `rmtree` on deep tree | ✅ delete events, no crash | | `test_non_existent_dir` | ✅ raises `NotADirectoryError` | | `test_callback_error_doesnt_crash` | ✅ watcher survives callback exception | | `test_deep_directory_tree` – `os.makedirs` 4 levels deep | ✅ file in innermost dir detected | | `test_many_files` – 20 files in parallel | ✅ all 20 created events captured | All 9/9 tests pass.
{"model": "python3.11", "problem_class": "io-watch-directory-recursive", "result": "passed", "tests": 9}