EntitySession API¶
Stateful per-entity FSM wrapper. Tracks current state, context, and history for a single entity.
See EntitySession guide for the full guide.
Classes¶
EntitySession
¶
EntitySession(machine: StateMachine, *, entity_id: str | None = None, context: dict[str, Any] | None = None, initial_state: str | None = None, defer_unhandled: bool = False)
Stateful wrapper around a :class:StateMachine.
Tracks the current state and context for a single entity (e.g., one order, one user session). Provides:
send(trigger, **payload)/asend(trigger, **payload)- Auto-generated trigger methods (e.g.,
order.confirm()) - Auto-generated
is_<state>properties (e.g.,order.is_pending) current_state,context,is_final,workflow_status,allowed_triggerssnapshot()for serializing the instance state (includesworkflow_status).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
machine
|
StateMachine
|
The StateMachine definition to use. |
required |
entity_id
|
str | None
|
Optional unique identifier (auto-generated UUID if omitted). |
None
|
context
|
dict[str, Any] | None
|
Optional initial context dict. |
None
|
initial_state
|
str | None
|
Override the initial state (defaults to machine's initial). |
None
|
defer_unhandled
|
bool
|
If True, queue unmatched events for later replay. |
False
|
Source code in src/pystator/instance.py
workflow_status
property
¶
FSM workflow outcome: active, complete, or failed.
Derived from the current state's type (TERMINAL → complete,
ERROR → failed, else active). Same semantics as persisted
workflow_status on entity store rows.
allowed_triggers
property
¶
Trigger names available from the current state.
allowed_transitions
property
¶
Transition definitions available from the current state.
history
property
¶
List of all transition results applied to this instance.
deferred_events
property
¶
Pending deferred events (trigger, payload) not yet applied.
send
¶
Send an event to the machine and apply the transition.
Thread-safe: uses an internal lock to serialize access.
When defer_unhandled=True was set, events that don't match any
transition are queued and replayed after the next successful state
change.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trigger
|
str
|
Event trigger name. |
required |
**payload
|
Any
|
Event payload data (merged into context for guards/actions). |
{}
|
Returns:
| Type | Description |
|---|---|
TransitionResult
|
TransitionResult describing the outcome. |
Source code in src/pystator/instance.py
asend
async
¶
Async version of :meth:send (supports async guards).
Async-safe: uses an internal asyncio.Lock to serialize access.
Source code in src/pystator/instance.py
apply_data
¶
Update context and evaluate when-route transitions.
Merges data into the session context, then runs the post-transition
evaluation chain (auto-transitions + when-routes). No trigger is
required -- the destination states' when clauses determine which
transition (if any) fires.
Requires classification.auto_route: true in the machine meta.
Returns:
| Type | Description |
|---|---|
list[TransitionResult]
|
List of transition results that fired (may be empty). |
Source code in src/pystator/instance.py
aapply_data
async
¶
Async version of :meth:apply_data.
Source code in src/pystator/instance.py
evaluate_destination
¶
evaluate_destination(destination_state: str, context_overrides: dict[str, Any] | None = None) -> DestinationCheckResult
Check if destination_state is reachable from :attr:current_state.
Merges context_overrides into :attr:context for the check (session
context is not mutated). Same semantics as
:meth:StateMachine.evaluate_destination.
Source code in src/pystator/instance.py
can_transition_to
¶
True if destination_state is reachable from the current state now.
Source code in src/pystator/instance.py
clear_deferred
¶
snapshot
¶
Serialize instance state for persistence.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with current_state, context, and deferred events (serializable). |
Source code in src/pystator/instance.py
from_snapshot
classmethod
¶
from_snapshot(machine: StateMachine, snapshot: dict[str, Any]) -> EntitySession
Restore an instance from a snapshot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
machine
|
StateMachine
|
The StateMachine definition. |
required |
snapshot
|
dict[str, Any]
|
Dict from :meth: |
required |
Returns:
| Type | Description |
|---|---|
EntitySession
|
Restored EntitySession. |
Source code in src/pystator/instance.py
record
¶
record() -> EventRecorder
EventRecorder
¶
EventRecorder(instance: EntitySession)
Records events sent to an EntitySession for later replay.
Example::
order = machine.create()
recorder = order.record()
recorder.send("confirm", valid=True)
recorder.send("ship")
# Replay on a fresh instance
replayed = recorder.replay(machine)
assert replayed.current_state == order.current_state
Source code in src/pystator/instance.py
send
¶
Send an event (delegates to instance) and record it.
Source code in src/pystator/instance.py
replay
¶
replay(machine: StateMachine) -> EntitySession
Create a fresh instance and replay all recorded events.
Source code in src/pystator/instance.py
to_dict
¶
Serialize the recording for storage.
from_dict
classmethod
¶
from_dict(data: dict[str, Any], machine: StateMachine) -> EventRecorder
Deserialize a recording and attach to a fresh instance.
Source code in src/pystator/instance.py
RecordedEvent
dataclass
¶
A single recorded event with timestamp.
Import¶
# Recommended — via StateMachine factory
from pystator import StateMachine
machine = StateMachine.from_yaml("fsm.yaml")
session = machine.create(context={"id": "entity-1"})
# Direct import
from pystator import EntitySession
from pystator.instance import EntitySession, EventRecorder, RecordedEvent
Auto-generated methods and properties¶
EntitySession dynamically generates, for each state machine definition:
is_<state_name>— boolean property;Truewhen the current state matches<trigger_name>(**payload)— sends the named trigger (equivalent to.send(trigger, **payload))
YAML state and trigger names must already be valid lowercase identifiers (a–z, digits, underscores). EntitySession exposes them as:
- is_<state> — e.g. state pending_new → is_pending_new
- <trigger>(**payload) — e.g. trigger exchange_ack → exchange_ack()
See also¶
- EntitySession guide
- Orchestrator — multi-entity session management with persistence
- Stores