-
-
Notifications
You must be signed in to change notification settings - Fork 103
Expand file tree
/
Copy pathstatechart_compound_machine.py
More file actions
97 lines (73 loc) · 2.94 KB
/
statechart_compound_machine.py
File metadata and controls
97 lines (73 loc) · 2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""
Compound states -- Quest through Middle-earth
==============================================
This example demonstrates compound (hierarchical) states using ``StateChart``.
A compound state contains inner child states, allowing you to model nested behavior.
When a compound state is entered, both the parent and its initial child become active.
Transitions within a compound change the active child while the parent stays active.
Exiting a compound removes all descendants.
"""
from statemachine import State
from statemachine import StateChart
class QuestMachine(StateChart):
"""A quest through Middle-earth with compound states.
The journey has two compound regions: the ``shire`` (with locations to visit)
and ``rivendell`` (with council activities). A ``wilderness`` state connects them.
"""
class shire(State.Compound):
bag_end = State("Bag End", initial=True)
green_dragon = State("The Green Dragon")
visit_pub = bag_end.to(green_dragon)
class rivendell(State.Compound):
council = State("Council of Elrond", initial=True)
forging = State("Reforging Narsil", final=True)
begin_forging = council.to(forging)
wilderness = State("Wilderness")
destination = State("Quest continues", final=True)
depart_shire = shire.to(wilderness)
arrive_rivendell = wilderness.to(rivendell) # type: ignore[arg-type]
done_state_rivendell = rivendell.to(destination)
# %%
# Starting the quest
# ------------------
#
# When the machine starts, the ``shire`` compound and its initial child ``bag_end``
# are both active.
sm = QuestMachine()
print(f"Active states: {sorted(sm.configuration_values)}")
assert {"shire", "bag_end"} == set(sm.configuration_values)
# %%
# Transitioning within a compound
# --------------------------------
#
# Moving within a compound changes the active child. The parent stays active.
sm.send("visit_pub")
print(f"After visiting pub: {sorted(sm.configuration_values)}")
assert "shire" in sm.configuration_values
assert "green_dragon" in sm.configuration_values
assert "bag_end" not in sm.configuration_values
# %%
# Exiting a compound
# -------------------
#
# Leaving a compound removes the parent and all children.
sm.send("depart_shire")
print(f"In the wilderness: {sorted(sm.configuration_values)}")
assert {"wilderness"} == set(sm.configuration_values)
# %%
# Entering another compound
# --------------------------
#
# Entering ``rivendell`` activates its initial child ``council``.
sm.send("arrive_rivendell")
print(f"At Rivendell: {sorted(sm.configuration_values)}")
assert {"rivendell", "council"} == set(sm.configuration_values)
# %%
# done.state event
# ------------------
#
# When the final child of a compound is reached, a ``done.state.{parent}`` event
# fires automatically, triggering the transition to ``destination``.
sm.send("begin_forging")
print(f"Quest continues: {sorted(sm.configuration_values)}")
assert {"destination"} == set(sm.configuration_values)