Note
Go to the end to download the full example code.
In() guard condition – Fellowship Coordination¶
This example demonstrates the In() guard condition using StateChart
with parallel states.
In('state_id') checks whether a given state is currently active. This is
especially useful in parallel regions where one region’s transitions depend
on the state of another region.
from statemachine import State
from statemachine import StateChart
class FellowshipMachine(StateChart):
"""Fellowship coordination with parallel regions.
Two parallel regions track Frodo and Sam independently. The key
transition -- ``sam_to_mordor`` -- uses ``In('mordor_f')`` to ensure Sam
only follows Frodo to Mordor after Frodo has already arrived there.
"""
class quest(State.Parallel):
class frodo_path(State.Compound):
shire_f = State("Frodo in Shire", initial=True)
rivendell_f = State("Frodo at Rivendell")
mordor_f = State("Frodo in Mordor", final=True)
frodo_to_rivendell = shire_f.to(rivendell_f)
frodo_to_mordor = rivendell_f.to(mordor_f)
class sam_path(State.Compound):
shire_s = State("Sam in Shire", initial=True)
rivendell_s = State("Sam at Rivendell")
mordor_s = State("Sam in Mordor")
mount_doom_s = State("Sam at Mount Doom", final=True)
sam_to_rivendell = shire_s.to(rivendell_s)
# Sam can only go to Mordor when Frodo is already there
sam_to_mordor = rivendell_s.to(mordor_s, cond="In('mordor_f')")
sam_to_mount_doom = mordor_s.to(mount_doom_s)
victory = State("Victory", final=True)
done_state_quest = quest.to(victory)
Initial state – both in the Shire¶
sm = FellowshipMachine()
vals = set(sm.configuration_values)
print(f"Start: {sorted(vals)}")
assert "shire_f" in vals
assert "shire_s" in vals
Start: ['frodo_path', 'quest', 'sam_path', 'shire_f', 'shire_s']
Move both to Rivendell independently¶
sm.send("frodo_to_rivendell")
sm.send("sam_to_rivendell")
vals = set(sm.configuration_values)
print(f"Both at Rivendell: {sorted(vals)}")
assert "rivendell_f" in vals
assert "rivendell_s" in vals
Both at Rivendell: ['frodo_path', 'quest', 'rivendell_f', 'rivendell_s', 'sam_path']
Sam can’t go to Mordor yet – In(‘mordor_f’) is false¶
Frodo hasn’t reached Mordor, so In('mordor_f') evaluates to false
and Sam’s transition is blocked.
sm.send("sam_to_mordor")
vals = set(sm.configuration_values)
print(f"Sam blocked: {sorted(vals)}")
assert "rivendell_s" in vals # Sam still at Rivendell
Sam blocked: ['frodo_path', 'quest', 'rivendell_f', 'rivendell_s', 'sam_path']
Frodo reaches Mordor – now Sam can follow¶
After Frodo transitions to mordor_f, the In('mordor_f') condition
becomes true. Now sending sam_to_mordor will succeed.
sm.send("frodo_to_mordor")
vals = set(sm.configuration_values)
print(f"Frodo in Mordor: {sorted(vals)}")
assert "mordor_f" in vals
assert "rivendell_s" in vals # Sam still waiting
Frodo in Mordor: ['frodo_path', 'mordor_f', 'quest', 'rivendell_s', 'sam_path']
Sam follows Frodo – In() guard passes¶
sm.send("sam_to_mordor")
vals = set(sm.configuration_values)
print(f"Sam follows: {sorted(vals)}")
assert "mordor_s" in vals
Sam follows: ['frodo_path', 'mordor_f', 'mordor_s', 'quest', 'sam_path']
Both regions complete – done.state fires¶
When both parallel regions reach their final states, done.state.quest
fires automatically and transitions to victory.
sm.send("sam_to_mount_doom")
print(f"Victory: {sorted(sm.configuration_values)}")
assert "victory" in sm.configuration_values
Victory: ['victory']