EDRI¶
The EDRI class is the main orchestrator. A single instance wires together the Router, Managers, Broker, API, Store, Scheduler, Connector, and Watcher into a running application.
Overview¶
You create one EDRI() in your entry-point script, add Managers, start whatever services you need, then call run(). The class handles startup ordering, graceful shutdown on SIGINT / SIGTERM, and cleanup of all child processes.
from edri import EDRI
edri = EDRI()
edri.add_component(...)
edri.start_api()
edri.run()
API surface¶
EDRI()¶
Constructor — takes no arguments (for now).
edri = EDRI()
Initialises the shared multiprocessing.Queue that the Router, Managers, and all internal components use to pass events.
add_component(manager)¶
Register a ManagerBase subclass.
edri.add_component(MyManager(edri.router_queue))
The Manager will be started when run() is called. Each component's signature is checked for the restart kwargs that the HealthChecker needs; a warning is logged if any are missing.
start_store(path=None, *, failure_policy=PersistenceFailurePolicy.BUFFER, startup_timeout=30.0)¶
Start the built-in key-value Store (a specialised Manager). Optional — only call if your application needs it.
Pass path="/var/lib/edri/store.db" for asynchronous SQLite persistence and
startup restoration. The default path=None keeps all KV data in memory. See
Store persistence for
failure handling, TTL, shutdown, and trusted-pickle requirements.
Persistent startup waits up to startup_timeout seconds for restoration;
increase it for a large database, or use None for no parent deadline.
edri.start_store()
See Specialized managers for usage.
start_scheduler(jobs=None)¶
Start the Scheduler with optional initial Job instances, given either as a list or as a mapping whose keys become the job identifiers.
edri.start_scheduler(jobs=[...])
edri.start_scheduler(jobs={"heartbeat": ...})
The Scheduler emits events at configured times/intervals. See Specialized managers.
start_broker(middlewares=None)¶
Start the Broker component that runs the middleware pipeline. Usually called implicitly by start_api().
edri.start_broker(middlewares=[...])
start_api(middlewares=None, json_encoder=None, jinja_extensions=None)¶
Start the ASGI Listener (API). This is the primary way to expose HTTP endpoints.
edri.start_api()
Parameters:
middlewares— list ofMiddlewareinstances applied to every request/response.json_encoder— customjson.JSONEncodersubclass (defaults to EDRI'sCustomJSONEncoder).jinja_extensions— set of Jinja2Extensionclasses for the HTML handler.
If start_broker has not been called yet, start_api calls it first.
start_grpc(host=None, port=None)¶
Start the gRPC listener — EDRI's gRPC front end, alongside or instead of start_api()'s ASGI Listener. Runs as its own process, sharing the same Broker (started here if it hasn't been already).
edri.start_grpc()
Parameters:
host— interface to bind, defaults toEDRI_GRPC_HOST/localhost.port— port to bind, defaults toEDRI_GRPC_PORT/50051.
If start_broker has not been called yet, start_grpc calls it first. See gRPC.
start_connector(router_id=None)¶
Connect to the distributed Switch for cross-machine event routing.
from uuid import UUID
edri.start_connector(router_id=UUID("..."))
start_watcher()¶
Enable the development hot-reload watcher. Watches source files and restarts component processes when they change.
edri.start_watcher()
See Watcher (Hot Reload).
run()¶
Enter the main loop. This method:
Creates the
Router.Starts all registered Managers (calls each component's
start()).Runs the Router's event-dispatch loop.
On
KeyboardInterrupt(SIGINT) or SIGTERM, shuts down gracefully: quits the Router, stops every Manager, and terminates all child processes (Store, Scheduler, Broker, Listener, Connector).
edri.run()
Startup order¶
sequenceDiagram
participant Main as Main thread
participant Router
participant Store
participant Scheduler
participant Broker
participant Listener
participant Connector
participant Mgrs as Managers
Main->>Main: EDRI()
Main->>Main: add_component() × N
Main->>Store: start_store() (optional)
Main->>Scheduler: start_scheduler() (optional)
Main->>Broker: start_broker() (optional)
Main->>Listener: start_api() (optional)
Main->>Connector: start_connector() (optional)
Main->>Main: start_watcher() (optional)
Main->>Router: run()
Router->>Mgrs: component.start(router_queue)
Router->>Router: event dispatch loop
Note over Router: waits for KeyboardInterrupt / SIGTERM
Router-->>Main: quit signal
Main->>Mgrs: component.quit()
Main->>(+)Store: terminate()
Main->>(+)Scheduler: terminate()
Main->>(+)Broker: terminate()
Main->>(+)Listener: terminate()
Main->>(+)Connector: terminate()
Minimal complete example¶
from datetime import datetime
from edri import EDRI
from edri.api.dataclass.api_event import api, ApiType
from edri.dataclass.event import Event
from edri.dataclass.response import Response, response
from edri.abstract import ManagerBase, request
from http import HTTPMethod
@response
class GreetingResponse(Response):
message: str
timestamp: str
@api(url="/hello/{name}", method=HTTPMethod.GET, exclude={ApiType.HTML, ApiType.WS})
class Greeting(Event):
name: str
response: GreetingResponse
class GreetingManager(ManagerBase):
@request
def greet(self, event: Greeting) -> None:
event.response.message = f"Hello, {event.name}!"
event.response.timestamp = datetime.now().isoformat()
if __name__ == "__main__":
edri = EDRI()
edri.add_component(GreetingManager(edri.router_queue))
edri.start_api()
edri.run()
Distributed example (with Connector)¶
from uuid import UUID
from edri import EDRI
from edri.abstract import ManagerBase, request
from my_events import RemoteEvent
class LocalManager(ManagerBase):
@request
def handle_remote(self, event: RemoteEvent) -> None:
print(f"Received from remote node: {event.data}")
if __name__ == "__main__":
edri = EDRI()
edri.add_component(LocalManager(edri.router_queue))
edri.start_connector(router_id=UUID("12345678-1234-5678-1234-567812345678"))
edri.run()
See also¶
Getting Started — step-by-step tutorial
Manager — Manager base classes
Router — event dispatching
Connector — distributed routing
Switch — the TCP hub
Watcher (Hot Reload) — hot-reload