Skip to content

Commit 5a84c3d

Browse files
Change FileWatcher to ignore watchfile heartbeat timeout.
The rust_timeout parameter in awatch() controls how long the underlying Rust code waits for file changes before checking if it should stop. In result in non-windows system missleading debug log was printed every 5 seconds: ` DEBUG watchfiles.main:267: rust notify timeout, continuing` Set `rust_timeout` to sys.maxsize - 1 as we don't need frequent timeouts for proper interrupt handling on non-Windows systems. yield_on_timeout was changed to True to yield empty set on this timeout error instead of printing debug log. This is safe because: - Unix systems use native file system events (inotify/FSEvents) - Process interruption works reliably without shorter timeouts - The large timeout value doesn't affect file change detection performance
1 parent b3c98e5 commit 5a84c3d

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

src/frequenz/channels/file_watcher.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import asyncio
2222
import pathlib
23+
import sys
2324
from collections import abc
2425
from dataclasses import dataclass
2526
from datetime import timedelta
@@ -159,6 +160,10 @@ def __init__(
159160
watch_filter=self._filter_events,
160161
force_polling=force_polling,
161162
poll_delay_ms=int(polling_interval.total_seconds() * 1_000),
163+
# Two arguments below are ok for non-Windows systems.
164+
# For more details, refer awatch documentation.
165+
rust_timeout=sys.maxsize - 1,
166+
yield_on_timeout=True,
162167
)
163168
self._awatch_stopped_exc: Exception | None = None
164169
self._changes: set[FileChange] = set()
@@ -204,11 +209,16 @@ async def ready(self) -> bool:
204209
if self._awatch_stopped_exc is not None:
205210
return False
206211

207-
try:
208-
self._changes = await anext(self._awatch)
209-
except StopAsyncIteration as err:
210-
self._awatch_stopped_exc = err
211-
return False
212+
# awatch will yield an empty set if rust notify timeout is reached.
213+
# This is safety heartbeat mechanism for Windows.
214+
# But on non-Windows systems, we don't need it, so we can ignore it.
215+
# For more details, refer awatch documentation.
216+
while len(self._changes) == 0:
217+
try:
218+
self._changes = await anext(self._awatch)
219+
except StopAsyncIteration as err:
220+
self._awatch_stopped_exc = err
221+
return False
212222

213223
return True
214224

tests/test_file_watcher.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66

77
import pathlib
8+
import sys
89
from collections.abc import AsyncGenerator, Iterator, Sequence
910
from typing import Any
1011
from unittest import mock
@@ -102,6 +103,8 @@ async def test_file_watcher_filter_events(
102103
watch_filter=filter_events,
103104
force_polling=True,
104105
poll_delay_ms=1_000,
106+
rust_timeout=sys.maxsize - 1,
107+
yield_on_timeout=True,
105108
)
106109
]
107110
for event_type in EventType:

0 commit comments

Comments
 (0)