0
# File Watching
1
2
Core file watching functionality providing both synchronous and asynchronous APIs for monitoring file system changes. Built on the high-performance Rust notify library with extensive configuration options for debouncing, filtering, and event handling.
3
4
## Capabilities
5
6
### Synchronous File Watching
7
8
Watch filesystem paths and yield sets of file changes as they occur. Supports multiple paths, recursive watching, and configurable debouncing to group rapid changes.
9
10
```python { .api }
11
def watch(
12
*paths: Union[Path, str],
13
watch_filter: Optional[Callable[[Change, str], bool]] = DefaultFilter(),
14
debounce: int = 1_600,
15
step: int = 50,
16
stop_event: Optional[AbstractEvent] = None,
17
rust_timeout: int = 5_000,
18
yield_on_timeout: bool = False,
19
debug: Optional[bool] = None,
20
raise_interrupt: bool = True,
21
force_polling: Optional[bool] = None,
22
poll_delay_ms: int = 300,
23
recursive: bool = True,
24
ignore_permission_denied: Optional[bool] = None,
25
) -> Generator[Set[FileChange], None, None]:
26
"""
27
Watch one or more paths and yield a set of changes whenever files change.
28
29
Parameters:
30
- *paths: Filesystem paths to watch (files or directories)
31
- watch_filter: Callable to filter changes, defaults to DefaultFilter()
32
- debounce: Maximum time in ms to group changes before yielding
33
- step: Time in ms to wait for new changes if at least one detected
34
- stop_event: Event to stop watching (any object with is_set() method)
35
- rust_timeout: Maximum time in ms to wait for changes in rust code
36
- yield_on_timeout: If True, yield empty set on timeout
37
- debug: Print filesystem changes to stdout (uses WATCHFILES_DEBUG env var if None)
38
- raise_interrupt: Whether to re-raise KeyboardInterrupt or suppress and stop
39
- force_polling: Force use of polling instead of native notifications
40
- poll_delay_ms: Delay between polls when force_polling=True
41
- recursive: Watch subdirectories recursively
42
- ignore_permission_denied: Ignore permission denied errors
43
44
Yields:
45
Set[FileChange]: Set of (Change, path) tuples representing file changes
46
47
Raises:
48
KeyboardInterrupt: If raise_interrupt=True and Ctrl+C pressed
49
"""
50
```
51
52
**Usage Examples:**
53
54
```python
55
from watchfiles import watch, Change
56
57
# Basic usage - watch current directory
58
for changes in watch('.'):
59
print(f'Changes detected: {changes}')
60
61
# Watch multiple paths with custom filter
62
from watchfiles import PythonFilter
63
64
for changes in watch('./src', './tests', watch_filter=PythonFilter()):
65
for change_type, path in changes:
66
if change_type == Change.added:
67
print(f'New file: {path}')
68
elif change_type == Change.modified:
69
print(f'Modified: {path}')
70
elif change_type == Change.deleted:
71
print(f'Deleted: {path}')
72
73
# Watch with stop event
74
import threading
75
76
stop_event = threading.Event()
77
threading.Timer(10.0, stop_event.set).start() # Stop after 10 seconds
78
79
for changes in watch('./src', stop_event=stop_event):
80
print(f'Changes: {changes}')
81
```
82
83
### Asynchronous File Watching
84
85
Async equivalent of the synchronous watch function, using threads internally with anyio for cross-platform async support.
86
87
```python { .api }
88
async def awatch(
89
*paths: Union[Path, str],
90
watch_filter: Optional[Callable[[Change, str], bool]] = DefaultFilter(),
91
debounce: int = 1_600,
92
step: int = 50,
93
stop_event: Optional[AnyEvent] = None,
94
rust_timeout: Optional[int] = None,
95
yield_on_timeout: bool = False,
96
debug: Optional[bool] = None,
97
raise_interrupt: Optional[bool] = None,
98
force_polling: Optional[bool] = None,
99
poll_delay_ms: int = 300,
100
recursive: bool = True,
101
ignore_permission_denied: Optional[bool] = None,
102
) -> AsyncGenerator[Set[FileChange], None]:
103
"""
104
Asynchronous version of watch() using threads.
105
106
Parameters:
107
Same as watch() except:
108
- stop_event: anyio.Event, asyncio.Event, or trio.Event
109
- rust_timeout: None means 1000ms on Windows, 5000ms elsewhere
110
- raise_interrupt: Deprecated, KeyboardInterrupt handled by event loop
111
112
Yields:
113
Set[FileChange]: Set of (Change, path) tuples representing file changes
114
115
Note:
116
KeyboardInterrupt cannot be suppressed and must be caught at the
117
asyncio.run() level or equivalent.
118
"""
119
```
120
121
**Usage Examples:**
122
123
```python
124
import asyncio
125
from watchfiles import awatch
126
127
async def monitor_files():
128
async for changes in awatch('./src', './tests'):
129
print(f'Async changes: {changes}')
130
131
# Run with proper KeyboardInterrupt handling
132
try:
133
asyncio.run(monitor_files())
134
except KeyboardInterrupt:
135
print('Monitoring stopped')
136
137
# With stop event
138
async def monitor_with_timeout():
139
stop_event = asyncio.Event()
140
141
# Stop after 10 seconds
142
async def stop_after_delay():
143
await asyncio.sleep(10)
144
stop_event.set()
145
146
stop_task = asyncio.create_task(stop_after_delay())
147
148
async for changes in awatch('./src', stop_event=stop_event):
149
print(f'Changes: {changes}')
150
151
await stop_task
152
153
asyncio.run(monitor_with_timeout())
154
```
155
156
### Force Polling Configuration
157
158
watchfiles automatically determines when to use polling vs native file system notifications, but this can be controlled manually:
159
160
**Automatic Force Polling Logic:**
161
- If `WATCHFILES_FORCE_POLLING` environment variable exists and is not empty:
162
- If value is `false`, `disable`, or `disabled`: force polling disabled
163
- Otherwise: force polling enabled
164
- Otherwise: force polling enabled only on WSL (Windows Subsystem for Linux)
165
166
**Polling Delay Configuration:**
167
- If `WATCHFILES_POLL_DELAY_MS` environment variable exists and is numeric: use that value
168
- Otherwise: use the `poll_delay_ms` parameter value (default 300ms)
169
170
**Example:**
171
172
```python
173
# Force polling with custom delay
174
for changes in watch('./src', force_polling=True, poll_delay_ms=100):
175
print(f'Polled changes: {changes}')
176
```
177
178
## Types
179
180
```python { .api }
181
# Type definitions used in file watching
182
FileChange = Tuple[Change, str]
183
184
class AbstractEvent(Protocol):
185
def is_set(self) -> bool: ...
186
187
# Union type for async events (TYPE_CHECKING only)
188
AnyEvent = Union[anyio.Event, asyncio.Event, trio.Event]
189
```