# 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 ```mermaid flowchart LR FS["Filesystem changes"] -->|FileModifiedEvent| Obs["watchdog.observers.Observer"] Obs --> WH["WatcherHandler
lookup component
by file path"] WH --> T["Timer debounce
1 s dev
10 s prod"] T --> RE["Restart Event → Router Queue"] ``` ### Component tree | Class | Role | |----------------------|------------------------------------------------------------------| | `Watcher` | Entry point — sets up `watchdog.observers.Observer`, schedules file paths | | `WatcherHandler` | Handles `FileModifiedEvent`, debounces with a `Timer` | | `WatcherHandlerComponent` | Dataclass: `(type: Type[ManagerBase], timer: Timer \| None)` | ### 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: 1. On first modification, starting a `Timer` with a delay. 2. On subsequent modifications before the timer fires, **cancelling** the old timer and starting a new one. 3. Only when the timer finally fires is the `Restart` event actually sent to the Router. This prevents restarting the Manager multiple times during a single save operation. --- ## Starting the Watcher ```python 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: 1. The Watcher puts a `Restart(manager=ManagerClass)` event on the Router queue. 2. The Router dispatches it to every Manager subscribed to `Restart`. Each Manager's built-in `_restart` handler checks whether `event.manager` matches its own class and ignores the event otherwise. 3. 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 `Restart` event 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 | |------------------|--------|--------------------------------------------------------------| | `development` | 1 s | Fast feedback during active development | | `production` | 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 | |---------------|---------------------------------------| | `ENVIRONMENT` | Controls the debounce delay (1 vs 10) | No additional environment variables are needed for basic operation. --- ## Related * {doc}`/manager` — Manager lifecycle, including `after_start()` hook * {doc}`/env` — `ENVIRONMENT` variable * {doc}`/router` — how `Restart` events are routed