-
-
Notifications
You must be signed in to change notification settings - Fork 133
Description
Description
The debounce and step parameters for the watcher generator appear to be malfunctioning on my system, or I might be misunderstanding their intended behavior.
According to the API docs:
| Name | Type | Description | Default |
|---|---|---|---|
debounce |
int |
maximum time in milliseconds to group changes over before yielding them. | 1600 |
step |
int |
time to wait for new changes in milliseconds, if no changes are detected in this time, and at least one change has been detected, the changes are yielded. | 50 |
My interpretation is that once a modification is detected, the watcher should wait for a full quiet period equal to step before yielding. If such a quiet period never occurs, the debounce interval should act as a maximum latency cap, causing the accumulated changes to be yielded regardless of ongoing activity. In other words, continuously modifying a file should cause the step timer to reset indefinitely, and only the expiration of debounce should trigger a yield.
However, what I am observing is the opposite:
debouncenever appears to trigger a yield.stepyields a singleset[tuple]event exactly once, after the configured number of milliseconds from the first modification, even if the file continues to be modified repeatedly within that interval.
Below is the code used for testing:
import watchfiles
for changes in watchfiles.watch(
"testfile.txt",
debounce=2_000,
step=4_000,
debug=True,
):
print("Yielding:", changes)I start this with:
uv run --with watchfiles teststep.pyIn a separate terminal window, I engage in the highly sophisticated testing approach of repeatedly issuing:
echo "test" >> testfile.txtThe watcher’s debug logs show that each write is detected in real time, but no events are yielded until step milliseconds have elapsed from the initial modification. Continuous writes do not reset the step timer.
The behavior is identical when using watch, awatch, or force_polling=True.
Example Code
Watchfiles Output
# uv run watchfiles 'echo reloaded' . --verbose
[22:19:09] watchfiles v1.1.1 👀 path="/current/path" target="echo reloaded" (command) filter=DefaultFilter...
[22:19:09] running "echo reloaded" as command
[22:19:09] registering handler for SIGTERM on watchfiles process 7214
watcher: FsEventWatcher { paths: 0x1027ae360, since_when: 18446744073709551615, latency: 0.0, flags: 18, event_handler: Pointer { addr: 0x1027ae330, metadata: DynMetadata(0x103a2cdb0) }, runloop: Some((0x887358c00, JoinHandle { .. })), recursive_info: {"/current/path": true} }
reloaded
[22:19:14] rust notify timeout, continuing
[22:19:19] rust notify timeout, continuing
[22:19:24] rust notify timeout, continuing
[22:19:29] rust notify timeout, continuing
[22:19:34] rust notify timeout, continuing
[22:19:39] rust notify timeout, continuing
[22:19:44] rust notify timeout, continuing
[22:19:49] rust notify timeout, continuing
[22:19:54] rust notify timeout, continuing
[22:19:59] rust notify timeout, continuing
[22:20:05] rust notify timeout, continuing
[22:20:10] rust notify timeout, continuing
[22:20:15] rust notify timeout, continuing
[22:20:20] rust notify timeout, continuing
[22:20:25] rust notify timeout, continuing
[22:20:30] rust notify timeout, continuing
[22:20:35] rust notify timeout, continuing
^C[22:20:38] KeyboardInterrupt caught, stopping watch
[22:20:38] process already dead, exit code: 0
Operating System & Architecture
macOS-26.2-arm64-arm-64bit-Mach-O
Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000
Environment
uv-managed Python installation
Python & Watchfiles Version
python: 3.14.0 (main, Oct 31 2025, 23:20:55) [Clang 21.1.4 ], watchfiles: 1.1.1
Rust & Cargo Version
No response