Skip to content

Commit 921808b

Browse files
committed
test: Nested states with general syntax
1 parent d1a0826 commit 921808b

File tree

4 files changed

+26
-20
lines changed

4 files changed

+26
-20
lines changed

statemachine/factory.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,8 @@ def _check(cls):
6868
if not has_states:
6969
raise InvalidDefinition(_("There are no states."))
7070

71-
# TODO: Validate no events if has nested states
72-
# if not has_events:
73-
# raise InvalidDefinition(_("There are no events."))
71+
if not has_events:
72+
raise InvalidDefinition(_("There are no events."))
7473

7574
cls._check_disconnected_state()
7675

@@ -117,8 +116,9 @@ def _add_unbounded_callback(cls, attr_name, func):
117116

118117
def add_state(cls, id, state: State):
119118
state._set_id(id)
120-
cls.states.append(state)
121-
cls.states_map[state.value] = state
119+
if not state.parent:
120+
cls.states.append(state)
121+
cls.states_map[state.value] = state
122122

123123
# also register all events associated directly with transitions
124124
for event in state.transitions.unique_events:

statemachine/state.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Any, Optional # noqa: F401, I001
1+
from typing import Any
2+
from typing import TypeAlias
23
from copy import deepcopy
34

45
from .callbacks import Callbacks
@@ -9,19 +10,22 @@
910

1011

1112
class NestedStateFactory(type):
12-
def __new__(cls, classname, bases, attrs, name=None, initial=False, parallel=False):
13+
def __new__( # type: ignore [misc]
14+
cls, classname, bases, attrs, name=None, initial=False, parallel=False
15+
) -> "State":
1316

1417
if not bases:
15-
return super().__new__(cls, classname, bases, attrs)
18+
return super().__new__(cls, classname, bases, attrs) # type: ignore [return-value]
1619

1720
substates = []
1821
for key, value in attrs.items():
19-
if not isinstance(value, State):
20-
continue
21-
value._set_id(key)
22-
substates.append(value)
22+
if isinstance(value, State):
23+
value._set_id(key)
24+
substates.append(value)
25+
if isinstance(value, TransitionList):
26+
value.add_event(key)
2327

24-
return State(name, initial=initial, parallel=parallel, substates=substates)
28+
return State(name=name, initial=initial, parallel=parallel, substates=substates)
2529

2630

2731
class NestedStateBuilder(metaclass=NestedStateFactory):
@@ -107,28 +111,28 @@ class State:
107111
108112
"""
109113

110-
Builder = NestedStateBuilder
114+
Builder: TypeAlias = NestedStateBuilder
111115

112116
def __init__(
113117
self,
114118
name: str = "",
115119
value: Any = None,
116120
initial: bool = False,
117121
final: bool = False,
118-
parallel=False,
119-
substates=None,
122+
parallel: bool = False,
123+
substates: Any = None,
120124
enter: Any = None,
121125
exit: Any = None,
122126
):
123127
self.name = name
124128
self.value = value
125129
self.parallel = parallel
126-
self.parent: "State" = None
127130
self.substates = substates or []
128131
self._initial = initial
129132
self._final = final
130133
self._id: str = ""
131134
self._storage: str = ""
135+
self.parent: "State" = None
132136
self.transitions = TransitionList()
133137
self.enter = Callbacks().add(enter)
134138
self.exit = Callbacks().add(exit)
@@ -137,6 +141,7 @@ def __init__(
137141
def _init_substates(self):
138142
for substate in self.substates:
139143
substate.parent = self
144+
setattr(self, substate.id, substate)
140145

141146
def __eq__(self, other):
142147
return (

tests/examples/microwave_inheritance_machine.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class on(State.Builder, name="On"):
5050
cooking.to(idle, cond="open.is_active")
5151
cooking.to.itself(internal=True, on="increment_timer")
5252

53+
assert isinstance(on, State) # so mypy stop complaining
5354
turn_off = on.to(off)
5455
turn_on = off.to(on)
5556
on.to(off, cond="cook_time_is_over") # eventless transition

tests/test_compound.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ class engine(State.Builder, name="Engine", initial=True):
1111
off = State("Off", initial=True)
1212
on = State("On")
1313

14-
turn_off = on.to(off)
1514
turn_on = off.to(on)
15+
turn_off = on.to(off)
1616

1717
return TestMachine
1818

@@ -26,8 +26,8 @@ def test_capture_constructor_arguments(self, compound_engine_cls):
2626

2727
def test_list_children_states(self, compound_engine_cls):
2828
sm = compound_engine_cls()
29-
assert [s.id for s in sm.engine.children] == ["off", "on"]
29+
assert [s.id for s in sm.engine.substates] == ["off", "on"]
3030

3131
def test_list_events(self, compound_engine_cls):
3232
sm = compound_engine_cls()
33-
assert [e.name for e in sm.events] == ["turn_off", "turn_on"]
33+
assert [e.name for e in sm.events] == ["turn_on", "turn_off"]

0 commit comments

Comments
 (0)