11from typing import TYPE_CHECKING
22from typing import Any
33from typing import Dict
4+ from typing import List
45from typing import Tuple
56from uuid import uuid4
67
@@ -31,7 +32,13 @@ def __init__(cls, name: str, bases: Tuple[type], attrs: Dict[str, Any]):
3132 cls .add_inherited (bases )
3233 cls .add_from_attributes (attrs )
3334
34- cls ._set_special_states ()
35+ try :
36+ cls .initial_state : State = next (s for s in cls .states if s .initial )
37+ except StopIteration :
38+ cls .initial_state = None # Abstract SM still don't have states
39+
40+ cls .final_states : List [State ] = [state for state in cls .states if state .final ]
41+
3542 cls ._check ()
3643
3744 if TYPE_CHECKING :
@@ -40,35 +47,6 @@ def __init__(cls, name: str, bases: Tuple[type], attrs: Dict[str, Any]):
4047 def __getattr__ (self , attribute : str ) -> Any :
4148 ...
4249
43- def _set_special_states (cls ):
44- if not cls .states :
45- return
46- initials = [s for s in cls .states if s .initial ]
47- if len (initials ) != 1 :
48- raise InvalidDefinition (
49- _ (
50- "There should be one and only one initial state. "
51- "Your currently have these: {!r}"
52- ).format ([s .id for s in initials ])
53- )
54- cls .initial_state = initials [0 ]
55- cls .final_states = [state for state in cls .states if state .final ]
56-
57- def _disconnected_states (cls , starting_state ):
58- visitable_states = set (visit_connected_states (starting_state ))
59- return set (cls .states ) - visitable_states
60-
61- def _check_disconnected_state (cls ):
62- disconnected_states = cls ._disconnected_states (cls .initial_state )
63- if disconnected_states :
64- raise InvalidDefinition (
65- _ (
66- "There are unreachable states. "
67- "The statemachine graph should have a single component. "
68- "Disconnected states: {}"
69- ).format ([s .id for s in disconnected_states ])
70- )
71-
7250 def _check (cls ):
7351 has_states = bool (cls .states )
7452 has_events = bool (cls ._events )
@@ -85,8 +63,21 @@ def _check(cls):
8563 if not has_events :
8664 raise InvalidDefinition (_ ("There are no events." ))
8765
66+ cls ._check_initial_state ()
67+ cls ._check_final_states ()
8868 cls ._check_disconnected_state ()
8969
70+ def _check_initial_state (cls ):
71+ initials = [s for s in cls .states if s .initial ]
72+ if len (initials ) != 1 :
73+ raise InvalidDefinition (
74+ _ (
75+ "There should be one and only one initial state. "
76+ "Your currently have these: {!r}"
77+ ).format ([s .id for s in initials ])
78+ )
79+
80+ def _check_final_states (cls ):
9081 final_state_with_invalid_transitions = [
9182 state for state in cls .final_states if state .transitions
9283 ]
@@ -98,6 +89,21 @@ def _check(cls):
9889 ).format ([s .id for s in final_state_with_invalid_transitions ])
9990 )
10091
92+ def _disconnected_states (cls , starting_state ):
93+ visitable_states = set (visit_connected_states (starting_state ))
94+ return set (cls .states ) - visitable_states
95+
96+ def _check_disconnected_state (cls ):
97+ disconnected_states = cls ._disconnected_states (cls .initial_state )
98+ if disconnected_states :
99+ raise InvalidDefinition (
100+ _ (
101+ "There are unreachable states. "
102+ "The statemachine graph should have a single component. "
103+ "Disconnected states: {}"
104+ ).format ([s .id for s in disconnected_states ])
105+ )
106+
101107 def add_inherited (cls , bases ):
102108 for base in bases :
103109 for state in getattr (base , "states" , []):
0 commit comments