Watcher (Hot Reload)¶
The Watcher provides hot-reload capability for Managers during development. It monitors Python source files for changes and automatically restarts the corresponding Manager when its file is modified.
The Watcher is a development-only feature. It is not intended for production use.
How it works¶
flowchart LR
FS["Filesystem changes"] -->|FileModifiedEvent| Obs["watchdog.observers.Observer"]
Obs --> WH["WatcherHandler<br/>lookup component<br/>by file path"]
WH --> T["Timer debounce<br/>1 s dev<br/>10 s prod"]
T --> RE["Restart Event → Router Queue"]
Component tree¶
Class |
Role |
|---|---|
|
Entry point — sets up |
|
Handles |
|
Dataclass: |
Debounce mechanism¶
When a file is saved (e.g. by an IDE that writes multiple times), multiple FileModifiedEvent can be emitted in rapid succession. The Watcher debounces by:
On first modification, starting a
Timerwith a delay.On subsequent modifications before the timer fires, cancelling the old timer and starting a new one.
Only when the timer finally fires is the
Restartevent actually sent to the Router.
This prevents restarting the Manager multiple times during a single save operation.
Starting the Watcher¶
from edri import EDRI
edri = EDRI()
edri.add_component(MyManager(edri.router_queue))
edri.start_watcher()
edri.run()
The Watcher automatically detects which Python files define the added components and monitors them all.
Restart behavior¶
When a file change is detected and the debounce timer expires:
The Watcher puts a
Restart(manager=ManagerClass)event on the Router queue.The Router dispatches it to every Manager subscribed to
Restart. Each Manager's built-in_restarthandler checks whetherevent.managermatches its own class and ignores the event otherwise.The matching Manager reloads its own source module (
importlib.reload), looks up the (possibly changed) class on the reloaded module, unsubscribes its events, starts a fresh instance of that class, and then exits.
Because the module is reloaded from disk, code changes in the Manager's file are picked up. The new Manager's __init__ runs again, so any in-memory state is rebuilt from scratch. Configuration read from environment variables or external sources is also re-read on restart.
Important: The
Restartevent carries the Manager class type (manager=...), not the instance. The restart is performed by the old Manager process itself — it reloads the class from disk and spawns the replacement before exiting; the Router only delivers the event.
Timer delays¶
Environment |
Delay |
Rationale |
|---|---|---|
|
1 s |
Fast feedback during active development |
|
10 s |
Prevent rapid restart loops if something is writing files |
The delay is set based on ENVIRONMENT environment variable.
Limitations¶
Only Managers added via
edri.add_component(...)are watched.The Watcher monitors the file that defines the Manager class. If the Manager depends on other modules that change, those changes are not detected unless those files also define a watched Manager.
Route registrations (event subscriptions) are re-applied on restart because the new Manager instance re-registers them during initialization.
The Watcher does not monitor files inside packages that are installed as dependencies (only project source files).
There is no support for monitoring multiple files per component — only the file containing the Manager class is watched.
Configuration¶
The Watcher uses watchdog under the hood. It is configured through environment variables:
Variable |
Effect on Watcher |
|---|---|
|
Controls the debounce delay (1 vs 10) |
No additional environment variables are needed for basic operation.