Skip to content

Event

Events are the messages that flow between entities. Every event has a target entity and a scheduled time.

Event types that form the fundamental units of simulation work.

Events drive the simulation forward. Each event represents something that happens at a specific point in simulation time. When invoked, an event calls its target entity's handle_event() method. For function-based dispatch, use Event.once() which wraps a function in a CallbackEntity.

This module also provides ProcessContinuation for generator-based multi-step processes, enabling entities to yield delays and resume execution later.

CompletionHook module-attribute

CompletionHook = Callable[
    [Instant], Union[list["Event"], "Event", None]
]

Signature for hooks that run when an event or process finishes.

Event

Event(
    time: Instant,
    event_type: str,
    target: Simulatable | None = None,
    *,
    daemon: bool = False,
    on_complete: list[CompletionHook] | None = None,
    context: dict[str, Any] | None = None,
)

The fundamental unit of simulation work.

Events are scheduled onto the EventHeap and processed in chronological order. Each event targets an Entity whose handle_event() method processes it.

For function-based dispatch without a full Entity, use the Event.once() static constructor which wraps a function in a CallbackEntity.

Events support two additional mechanisms:

  1. Generators: When handle_event() returns a generator, the simulation wraps it as a ProcessContinuation, enabling multi-step processes that yield delays between steps.

  2. Completion Hooks: Functions registered via on_complete run when the event finishes (including after generator exhaustion). Used for chaining actions or notifying dependent entities.

Sorting uses (time, insertion_order) to ensure deterministic FIFO ordering for events scheduled at the same instant.

Attributes:

Name Type Description
time

When this event should be processed.

event_type

Human-readable label for debugging and tracing.

daemon

If True, this event won't block auto-termination.

target

Entity to receive this event.

on_complete

Hooks to run when processing finishes.

context

Arbitrary metadata for tracing and debugging.

cancelled property

cancelled: bool

Whether this event has been cancelled.

cancel

cancel() -> None

Mark this event as cancelled. The simulation loop will skip it on pop.

Cancelling an already-cancelled or already-processed event is a no-op.

__repr__

__repr__() -> str

Return a concise representation showing time, type, and target.

trace

trace(action: str, **data: Any) -> None

Append a structured span to this event's application-level trace.

Parameters:

Name Type Description Default
action str

Short action name (e.g., "handle.start", "process.yield").

required
**data Any

Extra structured fields for debugging.

{}

add_completion_hook

add_completion_hook(hook: CompletionHook) -> None

Attach a function to run when this event finishes processing.

Completion hooks enable dependency chains and notification patterns. For example, a QueueDriver uses hooks to know when its target entity has finished processing work and is ready for more.

Parameters:

Name Type Description Default
hook CompletionHook

Function called with the finish time when processing completes.

required

invoke

invoke() -> list[Event]

Execute this event and return any resulting events.

Dispatches to the target entity's handle_event() method. If the handler returns a generator, it's automatically wrapped as a ProcessContinuation for multi-step execution.

Returns:

Type Description
list[Event]

New events to schedule, including any from completion hooks.

__lt__

__lt__(other: Event) -> bool
  1. Time (Primary)
  2. Insert Order (Secondary - guarantees FIFO for simultaneous events)

once staticmethod

once(
    time: Instant,
    event_type: str,
    fn: Callable[[Event], Any],
    *,
    daemon: bool = False,
    context: dict[str, Any] | None = None,
) -> Event

Create a one-shot event that invokes a function.

Wraps the function in a CallbackEntity so that all events use target-based dispatch uniformly.

Parameters:

Name Type Description Default
time Instant

When this event should fire.

required
event_type str

Human-readable label for debugging.

required
fn Callable[[Event], Any]

Function called with the event.

required
daemon bool

If True, won't block auto-termination.

False
context dict[str, Any] | None

Optional metadata dict.

None

ProcessContinuation

ProcessContinuation(
    time: Instant,
    event_type: str,
    target: Simulatable | None = None,
    *,
    daemon: bool = False,
    on_complete: list[CompletionHook] | None = None,
    context: dict[str, Any] | None = None,
    process: Generator | None = None,
)

Bases: Event

Internal event that resumes a paused generator-based process.

When an entity's handle_event() returns a generator, the simulation wraps it in a ProcessContinuation. Each invocation advances the generator to its next yield point, schedules another continuation for the yielded delay, and collects any side-effect events.

This enables entities to express multi-step, time-consuming operations naturally using Python's generator syntax:

def handle_event(self, event):
    yield 0.05  # Wait 50ms for network latency
    yield self.compute_time  # Wait for processing
    return self.create_response(event)

Yields are interpreted as: - yield delay - Wait for delay seconds before resuming - yield (delay, events) - Wait and also schedule side-effect events - yield SimFuture() - Park until the future is resolved

Attributes:

Name Type Description
process

The Python generator being executed incrementally.

invoke

invoke() -> list[Event]

Advance the generator to its next yield and schedule the continuation.

reset_event_counter

reset_event_counter() -> None

Reset the global event counter to zero.

Called by Simulation.init() so each simulation run gets deterministic sort indices starting from 0.

enable_event_tracing

enable_event_tracing() -> None

Enable application-level event tracing (stack + trace spans).

Called by the visual debugger's serve() to enable per-event trace recording. When disabled (the default), Event.invoke() skips trace() and _ensure_stack() calls for better performance.

disable_event_tracing

disable_event_tracing() -> None

Disable application-level event tracing.