Skip to content

Commit 7237dd5

Browse files
committed
feat: Queued mode preview
1 parent 6a82c36 commit 7237dd5

File tree

5 files changed

+71
-5
lines changed

5 files changed

+71
-5
lines changed

statemachine/event.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def _process(self, event_data):
4444
event_data.executed = True
4545
break
4646
else:
47-
raise TransitionNotAllowed(event_data.event, event_data.state)
47+
if not event_data.machine.queued:
48+
raise TransitionNotAllowed(event_data.event, event_data.state)
4849

4950

5051
def trigger_event_factory(event):

statemachine/state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
class NestedStateFactory(type):
1313
def __new__( # type: ignore [misc]
14-
cls, classname, bases, attrs, name=None, initial=False, parallel=False
14+
cls, classname, bases, attrs, name=None, **kwargs
1515
) -> "State":
1616

1717
if not bases:
@@ -25,7 +25,7 @@ def __new__( # type: ignore [misc]
2525
if isinstance(value, TransitionList):
2626
value.add_event(key)
2727

28-
return State(name=name, initial=initial, parallel=parallel, substates=substates)
28+
return State(name=name, substates=substates, **kwargs)
2929

3030

3131
class NestedStateBuilder(metaclass=NestedStateFactory):

statemachine/statemachine.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ class StateMachine(metaclass=StateMachineMetaclass):
2525
states = [] # type: List[State]
2626
states_map = {} # type: Dict[Any, State]
2727

28-
def __init__(self, model=None, state_field="state", start_value=None):
28+
def __init__(self, model=None, state_field="state", start_value=None, queued=False):
2929
self.model = model if model else Model()
3030
self.state_field = state_field
3131
self.start_value = start_value
32+
self.queued = queued
3233

3334
if self._abstract:
3435
raise InvalidDefinition(_("There are no states or transitions."))
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
Nested Traffic light machine
3+
----------------------------
4+
5+
Demonstrates the concept of nested compound states.
6+
7+
From this example on XState: https://xstate.js.org/docs/guides/hierarchical.html#api
8+
9+
"""
10+
import time
11+
12+
from statemachine import State
13+
from statemachine import StateMachine
14+
15+
16+
class NestedTrafficLightMachine(StateMachine):
17+
"A traffic light machine"
18+
green = State(initial=True, enter="reset_elapsed")
19+
yellow = State(enter="reset_elapsed")
20+
21+
class red(State.Builder, enter="reset_elapsed"):
22+
"Pedestrian states"
23+
walk = State(initial=True)
24+
wait = State()
25+
stop = State()
26+
blinking = State()
27+
28+
ped_countdown = walk.to(wait) | wait.to(stop)
29+
30+
assert isinstance(red, State)
31+
timer = green.to(yellow) | yellow.to(red) | red.to(green)
32+
power_outage = red.blinking.from_()
33+
power_restored = red.from_()
34+
35+
def __init__(self, seconds_to_turn_state=5, seconds_running=20):
36+
self.seconds_to_turn_state = seconds_to_turn_state
37+
self.seconds_running = seconds_running
38+
super().__init__(queued=True)
39+
40+
def on_timer(self, event: str, source: State, target: State):
41+
print(f".. Running {event} from {source.id} to {target.id}")
42+
43+
def reset_elapsed(self, event: str, time):
44+
print(f"entering reset_elapsed from {event} with {time}")
45+
self.last_turn = time if time else 0
46+
47+
@timer.cond
48+
def time_is_over(self, time):
49+
return time - self.last_turn > self.seconds_to_turn_state
50+
51+
def run_forever(self):
52+
self.running = True
53+
start_time = time.time()
54+
while self.running:
55+
print("tick!")
56+
time.sleep(1)
57+
curr_time = time.time()
58+
self.send("timer", time=curr_time)
59+
60+
if curr_time - start_time > self.seconds_running:
61+
self.running = False
62+
63+
64+
sm = NestedTrafficLightMachine()

tests/test_compound.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def assert_state(s, name, initial=False, final=False, parallel=False, substates=
2323
assert set(s.substates) == set(substates)
2424

2525

26-
class TestNestedDeclarations:
26+
class TestNestedSyntax:
2727
def test_capture_constructor_arguments(self, microwave_cls):
2828
sm = microwave_cls()
2929

0 commit comments

Comments
 (0)