Skip to content

Modules

A module implements an interface with concrete behavior. While interfaces declare what events a component handles, modules define how it responds to them. Modules are the active, running units of a PyRapide system.

Defining a Module

Use the @module decorator with the implements parameter to bind a class to an interface. The module provides lifecycle hooks and reactive event handlers.

module_example.py python
1from pyrapide import module, when, get_context, Pattern
2
3@module(implements=DatabaseService)
4class PostgresModule:
5    async def start(self):
6        ctx = get_context(self)
7        ctx.generate_event("DatabaseService.query",
8                           payload={"sql": "SELECT 1", "params": {}})
9
10    @when(Pattern.match("request.received"))
11    async def on_request(self, match):
12        ctx = get_context(self)
13        event = list(match.events)[0]
14        ctx.generate_event("DatabaseService.query",
15                           payload={"sql": event.payload["sql"],
16                                    "params": event.payload.get("params", {})},
17                           caused_by=list(match.events))

Lifecycle Hooks

Modules support two lifecycle hooks that run at predictable points in the system's execution:

  • start(): called when the module is initialized within an architecture. Use it for setup tasks like establishing connections, loading configuration, or emitting initial events.
  • finish(): called when the module is shutting down. Use it for cleanup such as closing connections or flushing buffers.
lifecycle.py python
@module(implements=DatabaseService)
class PostgresModule:
    async def start(self):
        # Initialize connection pool, emit readiness event
        ctx = get_context(self)
        self.pool = await create_pool(dsn="postgresql://...")
        ctx.generate_event("DatabaseService.query",
                           payload={"sql": "SELECT 1", "params": {}})

    async def finish(self):
        # Gracefully close the connection pool
        await self.pool.close()

Reactive Handlers with @when

The @when decorator registers a method as a reactive handler. When the specified pattern matches against incoming events, the handler is invoked with the match result.

when_handlers.py python
@module(implements=DatabaseService)
class PostgresModule:
    @when(Pattern.match("request.received"))
    async def on_request(self, match):
        """Handle incoming requests by issuing database queries."""
        ctx = get_context(self)
        event = list(match.events)[0]
        ctx.generate_event("DatabaseService.query",
                           payload={"sql": event.payload["sql"],
                                    "params": event.payload.get("params", {})},
                           caused_by=list(match.events))

    @when(Pattern.match("DatabaseService.error"))
    async def on_error(self, match):
        """Log and handle database errors."""
        event = list(match.events)[0]
        print(f"Database error: {event.payload['message']}")
💡 Tip
A module can have multiple @when handlers. Each handler reacts independently to its own pattern, allowing you to separate concerns cleanly within a single module.

Module Context

The get_context(self) function returns the module's runtime context, which provides:

  • generate_event(name, payload, caused_by): emit a new event into the system with optional causal links.
  • computation: access the current Computation to query the causal graph.
  • clock: access the module's clock for temporal reasoning.

Conformance Checking

âš  Warning
If a module claims to implement an interface but is missing required action handlers, PyRapide raises a ConformanceError at decoration time. This catches integration mistakes before your system starts.
conformance.py python
@module(implements=DatabaseService)
class IncompleteModule:
    async def start(self):
        pass
    # Missing handlers for query, result, error!
    # Raises ConformanceError: IncompleteModule does not conform to
    # DatabaseService - missing actions: query, result, error

The conformance check ensures that every action declared in the interface's alphabet has a corresponding handler in the module. This guarantee is enforced statically at decoration time, not at runtime.


To compose modules into a running system, see Architectures and Connections.