watchfiles
watchfiles is a file-watching library and CLI for Python. It uses Rust’s notify crate for filesystem event notifications and exposes both synchronous and asynchronous Python APIs. It is the file watcher behind uvicorn’s --reload mode (bundled in uvicorn[standard]).
Note
watchfiles 1.2.0 is the current stable release, licensed MIT. Requires Python 3.10+. Install with uv add --dev watchfiles or pip install watchfiles. Pre-built wheels are available for Linux (x86_64, aarch64, armv7l, musl), macOS (x86_64, aarch64), and Windows (x86_64, aarch64, i686). Building from source requires a Rust toolchain.
Key Features
- Filesystem monitoring backed by OS-native APIs (inotify on Linux, FSEvents on macOS, ReadDirectoryChanges on Windows) via the Rust notify crate, with automatic polling fallback
- Change batching and debouncing handled at the Rust level before events reach Python
- Synchronous API (
watch,run_process) and async API (awatch,arun_process) with anyio support - Built-in filters:
DefaultFilter(ignores common noise like.git,__pycache__,.pyc),PythonFilter(.pyfiles only), or custom filter functions - Standalone CLI for re-running any shell command on file changes
- Formerly named watchgod; a migration guide covers the rename
CLI
The CLI watches one or more paths and re-runs a command whenever a file changes:
watchfiles "python main.py" srcCommon flags:
| Flag | Purpose |
|---|---|
--filter python |
Only trigger on .py file changes |
--filter all |
Trigger on all file changes (no filtering) |
--ignore-paths dir1,dir2 |
Skip changes in specific directories |
--non-recursive |
Watch only the top-level directory, not subdirectories |
--sigint-timeout N |
Seconds to wait after SIGINT before sending SIGKILL |
--grace-period N |
Seconds after process start before watching for changes |
--verbose |
Set log level to debug |
The --target-type flag accepts command, function, or auto (the default). When set to function, the target is a dotted Python path (e.g., mypackage.main.run) executed in-process rather than as a subprocess.
Python API
The sync watch function yields sets of (Change, path) tuples as files are modified:
from watchfiles import watch, Change
for changes in watch("src"):
for change_type, path in changes:
print(change_type, path) # Change.modified, "src/main.py"run_process combines watching with subprocess management:
from watchfiles import run_process
run_process("src", target="python main.py")Both have async equivalents (awatch, arun_process) that work with asyncio and anyio.
Pros
- Fast: Rust-level event handling and debouncing avoids the overhead of polling in Python
- Pre-built wheels for all major platforms; no compilation needed for most users
- Async-native with both asyncio and anyio support
- Ships with sensible default filters that ignore
.git,__pycache__, and other noise - Battle-tested as uvicorn’s reload backend
Cons
- Building from source requires a Rust toolchain, which adds friction in environments without pre-built wheels
- Narrower API than watchdog (no granular event types, no custom event handler classes)
- Python 3.10+ only; projects supporting older Python versions need an alternative