15
15
from .statemachine import StateMachine
16
16
17
17
18
+ class NestedStateFactory (type ):
19
+ def __new__ ( # type: ignore [misc]
20
+ cls , classname , bases , attrs , name = None , ** kwargs
21
+ ) -> "State" :
22
+ if not bases :
23
+ return super ().__new__ (cls , classname , bases , attrs ) # type: ignore [return-value]
24
+
25
+ substates = []
26
+ for key , value in attrs .items ():
27
+ if isinstance (value , State ):
28
+ value ._set_id (key )
29
+ substates .append (value )
30
+ if isinstance (value , TransitionList ):
31
+ value .add_event (key )
32
+
33
+ return State (name = name , substates = substates , ** kwargs )
34
+
35
+
18
36
class State :
19
37
"""
20
38
A State in a :ref:`StateMachine` describes a particular behavior of the machine.
@@ -94,20 +112,47 @@ class State:
94
112
95
113
"""
96
114
115
+ class Builder (metaclass = NestedStateFactory ):
116
+ # Mimic the :ref:`State` public API to help linters discover the result of the Builder
117
+ # class.
118
+
119
+ @classmethod
120
+ def to (cls , * args : "State" , ** kwargs ) -> "TransitionList" : # pragma: no cover
121
+ """Create transitions to the given target states.
122
+
123
+ .. note: This method is only a type hint for mypy.
124
+ The actual implementation belongs to the :ref:`State` class.
125
+ """
126
+ return TransitionList ()
127
+
128
+ @classmethod
129
+ def from_ (cls , * args : "State" , ** kwargs ) -> "TransitionList" : # pragma: no cover
130
+ """Create transitions from the given target states (reversed).
131
+
132
+ .. note: This method is only a type hint for mypy.
133
+ The actual implementation belongs to the :ref:`State` class.
134
+ """
135
+ return TransitionList ()
136
+
97
137
def __init__ (
98
138
self ,
99
139
name : str = "" ,
100
140
value : Any = None ,
101
141
initial : bool = False ,
102
142
final : bool = False ,
143
+ parallel : bool = False ,
144
+ substates : Any = None ,
103
145
enter : Any = None ,
104
146
exit : Any = None ,
105
147
):
106
148
self .name = name
107
149
self .value = value
150
+ self .parallel = parallel
151
+ self .substates = substates or []
108
152
self ._initial = initial
109
153
self ._final = final
110
154
self ._id : str = ""
155
+ self .parent : "State" = None
111
156
self .transitions = TransitionList ()
112
157
self ._specs = CallbackSpecList ()
113
158
self .enter = self ._specs .grouper (CallbackGroup .ENTER ).add (
@@ -116,6 +161,12 @@ def __init__(
116
161
self .exit = self ._specs .grouper (CallbackGroup .EXIT ).add (
117
162
exit , priority = CallbackPriority .INLINE
118
163
)
164
+ self ._init_substates ()
165
+
166
+ def _init_substates (self ):
167
+ for substate in self .substates :
168
+ substate .parent = self
169
+ setattr (self , substate .id , substate )
119
170
120
171
def __eq__ (self , other ):
121
172
return isinstance (other , State ) and self .name == other .name and self .id == other .id
0 commit comments