watchfiles
Fast filesystem watching for Python — Rust-backed file change detection with both sync and async APIs. watchfiles features: watch() sync generator that yields sets of (Change, path) tuples, awatch() async generator for asyncio, run_process() for subprocess hot-reload, arun_process() async version, Change enum (added/modified/deleted), filter_func parameter for custom filtering, recursive watching with native OS events (inotify/FSEvents/kqueue), debouncing by default, and significantly faster than pure-Python watchdog for high-frequency changes.
Score Breakdown
⚙ Agent Friendliness
🔒 Security
Rust-backed filesystem watcher. Path traversal: validate that watched paths don't allow escape from intended directory. run_process() executes shell commands — ensure command string is not user-controlled. Symlinks: watchfiles follows symlinks by default — may expose unintended directories.
⚡ Reliability
Best When
Fast async-compatible file watching in asyncio applications and hot-reload scenarios — watchfiles is Rust-backed making it more reliable than pure Python alternatives.
Avoid When
Event handler class pattern (use watchdog), complex Windows integration, or when the sync-only watchdog API is already in use.
Use Cases
- • Agent sync file watch — from watchfiles import watch, Change; for changes in watch('/path/to/dir'): for change, path in changes: if change == Change.modified: process(path); print(f'{change}: {path}') — blocking watch; agent monitors directory synchronously; watch() blocks until changes; each iteration yields a set of (Change, path) tuples
- • Agent async file watch — from watchfiles import awatch; async def monitor(): async for changes in awatch('/path/to/dir'): for change, path in changes: await handle_change(change, path) — async watch; agent monitors files in asyncio context without blocking; awatch() is async generator; ideal for FastAPI/asyncio applications
- • Agent process hot-reload — from watchfiles import run_process; run_process('python server.py', watch_dir='src/') — hot reload; agent restarts subprocess when watched files change; run_process blocks main process; subprocess killed and restarted on any change; used by uvicorn --reload internally
- • Agent filtered watch — from watchfiles import watch, Change; def py_only(change, path): return path.endswith('.py'); for changes in watch('src/', watch_filter=py_only): handle(changes) — filtered; agent watches only specific file types; watch_filter receives (Change, path) and returns bool; replaces post-filtering logic
- • Agent change classification — from watchfiles import watch, Change; for changes in watch('.'): added = {p for c, p in changes if c == Change.added}; modified = {p for c, p in changes if c == Change.modified}; deleted = {p for c, p in changes if c == Change.deleted} — classify; agent processes different change types differently; Change.added/modified/deleted are the three event types
Not For
- • Complex event handler patterns — watchfiles yields changes as sets; for event handler class pattern use watchdog
- • Windows deep integration — watchfiles has good Windows support but watchdog's ReadDirectoryChangesW gives more event detail
- • Process-level file watching in production — for production monitoring use OS-level tools (systemd, inotifywait)
Interface
Authentication
No auth — filesystem watching library.
Pricing
watchfiles is MIT licensed. Created by Samuel Colvin (also created Pydantic). Free for all use.
Agent Metadata
Known Gotchas
- ⚠ Changes are a Set not ordered list — for changes in watch('.'): each iteration yields Set[tuple[Change, str]]; order within set is undefined; agent code: do not assume order of changes within one batch; process all changes in set before next iteration; use {p for c, p in changes if c == Change.modified} to extract paths
- ⚠ watch() is a blocking generator — for changes in watch('dir') blocks until first change, then blocks again; agent code: run in separate thread if main thread must continue; or use awatch() in async context; run_process() handles subprocess management separately
- ⚠ awatch() requires async for — async for changes in awatch('dir') — cannot use next() or list(); agent code using awatch() must be in async def function; cancel the async generator with generator.aclose() on shutdown; CTRL+C raises CancelledError in async context
- ⚠ Debouncing may combine rapid changes — watchfiles defaults to 300ms debounce; multiple rapid saves appear as one change set; agent code processing file sequences: debouncing is usually desirable but may cause missed events if processing takes longer than debounce window; force_polling=True disables native events and uses slower polling
- ⚠ run_process() path must be string command — run_process('python server.py') not run_process(['python', 'server.py']); shell=True implied; agent code: use string command; pass target= for function-based reload: run_process(target=my_function, args=(arg1,))
- ⚠ watch_filter receives Change and str not event object — watch_filter=lambda c, p: p.endswith('.py') — c is Change enum, p is string path; unlike watchdog's event objects; agent code migrating from watchdog: update filter function signature from (event) to (change, path); Change enum: Change.added, Change.modified, Change.deleted
Alternatives
Full Evaluation Report
Comprehensive deep-dive: security analysis, reliability audit, agent experience review, cost modeling, competitive positioning, and improvement roadmap for watchfiles.
AI-powered analysis · PDF + markdown · Delivered within 30 minutes
Package Brief
Quick verdict, integration guide, cost projections, gotchas with workarounds, and alternatives comparison.
Delivered within 10 minutes
Score Monitoring
Get alerted when this package's AF, security, or reliability scores change significantly. Stay ahead of regressions.
Continuous monitoring
Scores are editorial opinions as of 2026-03-06.