# watchfiles


watchfiles is a file-watching library and CLI for Python. It uses Rust's [notify](https://github.com/notify-rs/notify) crate for filesystem event notifications and exposes both synchronous and asynchronous Python APIs. It is the file watcher behind [uvicorn](https://www.uvicorn.org/)'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` (`.py` files only), or custom filter functions
- Standalone CLI for re-running any shell command on file changes
- Formerly named watchgod; a [migration guide](https://watchfiles.helpmanual.io/migrating/) covers the rename

## CLI

The CLI watches one or more paths and re-runs a command whenever a file changes:

```bash
watchfiles "python main.py" src
```

Common 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:

```python
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:

```python
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](https://python-watchdog.readthedocs.io/) (no granular event types, no custom event handler classes)
- Python 3.10+ only; projects supporting older Python versions need an alternative

## Learn More

- [watchfiles documentation](https://watchfiles.helpmanual.io/)
- [GitHub repository](https://github.com/samuelcolvin/watchfiles)
- [PyPI page](https://pypi.org/project/watchfiles/)
- [How Does Hot Reloading Work in Python?](https://pydevtools.com/handbook/explanation/how-does-hot-reloading-work-in-python.md)
- [How to Set Up Auto-Reload for Python Projects](https://pydevtools.com/handbook/how-to/how-to-set-up-auto-reload-for-python-projects.md)
