1515 from .statemachine import StateMachine
1616
1717
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+
1836class State :
1937 """
2038 A State in a :ref:`StateMachine` describes a particular behavior of the machine.
@@ -94,20 +112,47 @@ class State:
94112
95113 """
96114
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+
97137 def __init__ (
98138 self ,
99139 name : str = "" ,
100140 value : Any = None ,
101141 initial : bool = False ,
102142 final : bool = False ,
143+ parallel : bool = False ,
144+ substates : Any = None ,
103145 enter : Any = None ,
104146 exit : Any = None ,
105147 ):
106148 self .name = name
107149 self .value = value
150+ self .parallel = parallel
151+ self .substates = substates or []
108152 self ._initial = initial
109153 self ._final = final
110154 self ._id : str = ""
155+ self .parent : "State" = None
111156 self .transitions = TransitionList ()
112157 self ._specs = CallbackSpecList ()
113158 self .enter = self ._specs .grouper (CallbackGroup .ENTER ).add (
@@ -116,6 +161,12 @@ def __init__(
116161 self .exit = self ._specs .grouper (CallbackGroup .EXIT ).add (
117162 exit , priority = CallbackPriority .INLINE
118163 )
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 )
119170
120171 def __eq__ (self , other ):
121172 return isinstance (other , State ) and self .name == other .name and self .id == other .id
0 commit comments