vention-state-machine 0.4.0__py3-none-any.whl → 0.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- state_machine/core.py +7 -24
- state_machine/decorator_manager.py +3 -9
- state_machine/decorators.py +2 -8
- state_machine/defs.py +1 -3
- state_machine/machine_protocols.py +3 -9
- state_machine/utils.py +2 -5
- state_machine/vention_communication.py +1 -4
- {vention_state_machine-0.4.0.dist-info → vention_state_machine-0.4.2.dist-info}/METADATA +23 -7
- vention_state_machine-0.4.2.dist-info/RECORD +12 -0
- vention_state_machine-0.4.0.dist-info/RECORD +0 -12
- {vention_state_machine-0.4.0.dist-info → vention_state_machine-0.4.2.dist-info}/WHEEL +0 -0
state_machine/core.py
CHANGED
|
@@ -58,19 +58,12 @@ class StateMachine(HierarchicalGraphMachine):
|
|
|
58
58
|
) -> None:
|
|
59
59
|
# Normalize state definitions
|
|
60
60
|
if is_state_container(states):
|
|
61
|
-
state_groups = [
|
|
62
|
-
value
|
|
63
|
-
for value in vars(states).values()
|
|
64
|
-
if isinstance(value, StateGroup)
|
|
65
|
-
]
|
|
61
|
+
state_groups = [value for value in vars(states).values() if isinstance(value, StateGroup)]
|
|
66
62
|
resolved_states = [group.to_state_list()[0] for group in state_groups]
|
|
67
63
|
elif isinstance(states, list):
|
|
68
64
|
resolved_states = states
|
|
69
65
|
else:
|
|
70
|
-
raise TypeError(
|
|
71
|
-
f"`states` must be either a StateGroup container or a list of state dicts. "
|
|
72
|
-
f"Got: {type(states).__name__}"
|
|
73
|
-
)
|
|
66
|
+
raise TypeError(f"`states` must be either a StateGroup container or a list of state dicts. " f"Got: {type(states).__name__}")
|
|
74
67
|
|
|
75
68
|
self._declared_states: list[dict[str, Any]] = resolved_states
|
|
76
69
|
|
|
@@ -95,9 +88,7 @@ class StateMachine(HierarchicalGraphMachine):
|
|
|
95
88
|
self._timeouts: dict[str, asyncio.Task[Any]] = {}
|
|
96
89
|
self._last_state: Optional[str] = None
|
|
97
90
|
self._enable_recovery: bool = enable_last_state_recovery
|
|
98
|
-
self._history: deque[dict[str, Any]] = deque(
|
|
99
|
-
maxlen=history_size or self.DEFAULT_HISTORY_SIZE
|
|
100
|
-
)
|
|
91
|
+
self._history: deque[dict[str, Any]] = deque(maxlen=history_size or self.DEFAULT_HISTORY_SIZE)
|
|
101
92
|
self._current_start: Optional[datetime] = None
|
|
102
93
|
self._guard_conditions: dict[str, List[Callable[[], bool]]] = {}
|
|
103
94
|
self._state_change_callbacks: List[Callable[[str, str, str], None]] = []
|
|
@@ -110,9 +101,7 @@ class StateMachine(HierarchicalGraphMachine):
|
|
|
110
101
|
BaseStates.FAULT.value,
|
|
111
102
|
before="cancel_tasks",
|
|
112
103
|
)
|
|
113
|
-
self.add_transition(
|
|
114
|
-
BaseTriggers.RESET.value, BaseStates.FAULT.value, BaseStates.READY.value
|
|
115
|
-
)
|
|
104
|
+
self.add_transition(BaseTriggers.RESET.value, BaseStates.FAULT.value, BaseStates.READY.value)
|
|
116
105
|
self._attach_after_hooks()
|
|
117
106
|
self._add_guard_conditions_to_transitions()
|
|
118
107
|
|
|
@@ -137,9 +126,7 @@ class StateMachine(HierarchicalGraphMachine):
|
|
|
137
126
|
except asyncio.CancelledError:
|
|
138
127
|
pass
|
|
139
128
|
|
|
140
|
-
def set_timeout(
|
|
141
|
-
self, state_name: str, seconds: float, trigger_fn: Callable[[], str]
|
|
142
|
-
) -> None:
|
|
129
|
+
def set_timeout(self, state_name: str, seconds: float, trigger_fn: Callable[[], str]) -> None:
|
|
143
130
|
"""
|
|
144
131
|
Schedule a timeout: if `state_name` remains active after `seconds`, fire `trigger_fn()`.
|
|
145
132
|
"""
|
|
@@ -191,9 +178,7 @@ class StateMachine(HierarchicalGraphMachine):
|
|
|
191
178
|
"""Get the last `n` history entries."""
|
|
192
179
|
return list(self._history)[-n:] if n > 0 else []
|
|
193
180
|
|
|
194
|
-
def add_transition_condition(
|
|
195
|
-
self, trigger_name: str, condition_fn: Callable[[], bool]
|
|
196
|
-
) -> None:
|
|
181
|
+
def add_transition_condition(self, trigger_name: str, condition_fn: Callable[[], bool]) -> None:
|
|
197
182
|
"""
|
|
198
183
|
Add a guard condition to a transition trigger.
|
|
199
184
|
Multiple conditions can be added for the same trigger - ALL must pass for the transition to be allowed.
|
|
@@ -202,9 +187,7 @@ class StateMachine(HierarchicalGraphMachine):
|
|
|
202
187
|
self._guard_conditions[trigger_name] = []
|
|
203
188
|
self._guard_conditions[trigger_name].append(condition_fn)
|
|
204
189
|
|
|
205
|
-
def add_state_change_callback(
|
|
206
|
-
self, callback: Callable[[str, str, str], None]
|
|
207
|
-
) -> None:
|
|
190
|
+
def add_state_change_callback(self, callback: Callable[[str, str, str], None]) -> None:
|
|
208
191
|
"""
|
|
209
192
|
Add a callback that fires on any state change.
|
|
210
193
|
|
|
@@ -38,14 +38,10 @@ class DecoratorManager:
|
|
|
38
38
|
def _discover_state_callbacks(self, callback_fn: Any) -> None:
|
|
39
39
|
"""Discover on_enter_state and on_exit_state decorators."""
|
|
40
40
|
if hasattr(callback_fn, "_on_enter_state"):
|
|
41
|
-
self._decorator_bindings.append(
|
|
42
|
-
(callback_fn._on_enter_state, "enter", callback_fn)
|
|
43
|
-
)
|
|
41
|
+
self._decorator_bindings.append((callback_fn._on_enter_state, "enter", callback_fn))
|
|
44
42
|
|
|
45
43
|
if hasattr(callback_fn, "_on_exit_state"):
|
|
46
|
-
self._exit_decorator_bindings.append(
|
|
47
|
-
(callback_fn._on_exit_state, "exit", callback_fn)
|
|
48
|
-
)
|
|
44
|
+
self._exit_decorator_bindings.append((callback_fn._on_exit_state, "exit", callback_fn))
|
|
49
45
|
|
|
50
46
|
def _discover_guard_conditions(self, callback_fn: Any) -> None:
|
|
51
47
|
"""Discover guard decorators."""
|
|
@@ -70,9 +66,7 @@ class DecoratorManager:
|
|
|
70
66
|
|
|
71
67
|
def _bind_state_callbacks(self, instance: Any) -> None:
|
|
72
68
|
"""Bind state entry/exit callbacks."""
|
|
73
|
-
for state_name, hook_type, callback_fn in
|
|
74
|
-
self._decorator_bindings + self._exit_decorator_bindings
|
|
75
|
-
):
|
|
69
|
+
for state_name, hook_type, callback_fn in self._decorator_bindings + self._exit_decorator_bindings:
|
|
76
70
|
bound_fn = callback_fn.__get__(instance)
|
|
77
71
|
|
|
78
72
|
if hook_type == "enter" and hasattr(callback_fn, "_timeout_config"):
|
state_machine/decorators.py
CHANGED
|
@@ -54,9 +54,7 @@ def on_exit_state(
|
|
|
54
54
|
return decorator
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def auto_timeout(
|
|
58
|
-
seconds: float, trigger: Union[str, Callable[[], str]] = "to_fault"
|
|
59
|
-
) -> Callable[[CallableType], CallableType]:
|
|
57
|
+
def auto_timeout(seconds: float, trigger: Union[str, Callable[[], str]] = "to_fault") -> Callable[[CallableType], CallableType]:
|
|
60
58
|
"""
|
|
61
59
|
Decorator that applies an auto-timeout configuration to a state entry handler.
|
|
62
60
|
"""
|
|
@@ -90,11 +88,7 @@ def guard(
|
|
|
90
88
|
guard_fn._guard_conditions = {}
|
|
91
89
|
|
|
92
90
|
for trigger in triggers:
|
|
93
|
-
trigger_name = (
|
|
94
|
-
getattr(trigger, "name", trigger)
|
|
95
|
-
if hasattr(trigger, "name")
|
|
96
|
-
else trigger
|
|
97
|
-
)
|
|
91
|
+
trigger_name = getattr(trigger, "name", trigger) if hasattr(trigger, "name") else trigger
|
|
98
92
|
if not isinstance(trigger_name, str):
|
|
99
93
|
raise TypeError(f"Expected a Trigger or str, got {type(trigger)}")
|
|
100
94
|
|
state_machine/defs.py
CHANGED
|
@@ -88,9 +88,7 @@ class Trigger:
|
|
|
88
88
|
def __call__(self) -> str:
|
|
89
89
|
return self.name
|
|
90
90
|
|
|
91
|
-
def transition(
|
|
92
|
-
self, source: Union[str, State], dest: Union[str, State]
|
|
93
|
-
) -> Dict[str, str]:
|
|
91
|
+
def transition(self, source: Union[str, State], dest: Union[str, State]) -> Dict[str, str]:
|
|
94
92
|
"""
|
|
95
93
|
Build {"trigger": self.name, "source": <src>, "dest": <dst>}.
|
|
96
94
|
`source` / `dest` may be raw strings or StateKey instances.
|
|
@@ -2,9 +2,7 @@ from typing import Protocol, Callable, Any
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class SupportsTimeout(Protocol):
|
|
5
|
-
def set_timeout(
|
|
6
|
-
self, state_name: str, seconds: float, trigger_fn: Callable[[], str]
|
|
7
|
-
) -> None: ...
|
|
5
|
+
def set_timeout(self, state_name: str, seconds: float, trigger_fn: Callable[[], str]) -> None: ...
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class SupportsStateCallbacks(Protocol):
|
|
@@ -12,15 +10,11 @@ class SupportsStateCallbacks(Protocol):
|
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
class SupportsGuardConditions(Protocol):
|
|
15
|
-
def add_transition_condition(
|
|
16
|
-
self, trigger_name: str, condition_fn: Callable[[], bool]
|
|
17
|
-
) -> None: ...
|
|
13
|
+
def add_transition_condition(self, trigger_name: str, condition_fn: Callable[[], bool]) -> None: ...
|
|
18
14
|
|
|
19
15
|
|
|
20
16
|
class SupportsStateChangeCallbacks(Protocol):
|
|
21
|
-
def add_state_change_callback(
|
|
22
|
-
self, callback: Callable[[str, str, str], None]
|
|
23
|
-
) -> None: ...
|
|
17
|
+
def add_state_change_callback(self, callback: Callable[[str, str, str], None]) -> None: ...
|
|
24
18
|
|
|
25
19
|
|
|
26
20
|
class StateMachineProtocol(
|
state_machine/utils.py
CHANGED
|
@@ -48,11 +48,8 @@ def is_state_container(state_source: object) -> bool:
|
|
|
48
48
|
Return True if `obj` is an instance or class that contains StateGroup(s).
|
|
49
49
|
"""
|
|
50
50
|
try:
|
|
51
|
-
return any(
|
|
52
|
-
isinstance(value, StateGroup) for value in vars(state_source).values()
|
|
53
|
-
) or any(
|
|
54
|
-
isinstance(value, StateGroup)
|
|
55
|
-
for value in vars(state_source.__class__).values()
|
|
51
|
+
return any(isinstance(value, StateGroup) for value in vars(state_source).values()) or any(
|
|
52
|
+
isinstance(value, StateGroup) for value in vars(state_source.__class__).values()
|
|
56
53
|
)
|
|
57
54
|
except TypeError:
|
|
58
55
|
return False
|
|
@@ -116,10 +116,7 @@ def build_state_machine_rpc_bundle(
|
|
|
116
116
|
if trigger_name not in allowed:
|
|
117
117
|
raise ConnectError(
|
|
118
118
|
code="failed_precondition",
|
|
119
|
-
message=(
|
|
120
|
-
f"Trigger '{trigger_name}' cannot run from '{current}'. "
|
|
121
|
-
f"Allowed: {sorted(allowed)}"
|
|
122
|
-
),
|
|
119
|
+
message=(f"Trigger '{trigger_name}' cannot run from '{current}'. " f"Allowed: {sorted(allowed)}"),
|
|
123
120
|
)
|
|
124
121
|
|
|
125
122
|
# Fetch bound trigger method
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vention-state-machine
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Declarative state machine framework for machine apps
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: VentionCo
|
|
@@ -8,12 +8,28 @@ Requires-Python: >=3.10,<3.11
|
|
|
8
8
|
Classifier: License :: Other/Proprietary License
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Requires-Dist:
|
|
12
|
-
Requires-Dist:
|
|
13
|
-
Requires-Dist:
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist:
|
|
11
|
+
Requires-Dist: annotated-doc (==0.0.4) ; python_version == "3.10"
|
|
12
|
+
Requires-Dist: annotated-types (==0.7.0) ; python_version == "3.10"
|
|
13
|
+
Requires-Dist: anyio (==4.11.0) ; python_version == "3.10"
|
|
14
|
+
Requires-Dist: asyncio (==3.4.3) ; python_version == "3.10"
|
|
15
|
+
Requires-Dist: click (==8.1.8) ; python_version == "3.10"
|
|
16
|
+
Requires-Dist: colorama (==0.4.6) ; python_version == "3.10" and platform_system == "Windows"
|
|
17
|
+
Requires-Dist: coverage (==7.10.7) ; python_version == "3.10"
|
|
18
|
+
Requires-Dist: exceptiongroup (==1.3.0) ; python_version == "3.10"
|
|
19
|
+
Requires-Dist: fastapi (==0.121.1) ; python_version == "3.10"
|
|
20
|
+
Requires-Dist: graphviz (==0.21) ; python_version == "3.10"
|
|
21
|
+
Requires-Dist: h11 (==0.16.0) ; python_version == "3.10"
|
|
22
|
+
Requires-Dist: idna (==3.11) ; python_version == "3.10"
|
|
23
|
+
Requires-Dist: pydantic (==2.12.3) ; python_version == "3.10"
|
|
24
|
+
Requires-Dist: pydantic-core (==2.41.4) ; python_version == "3.10"
|
|
25
|
+
Requires-Dist: six (==1.17.0) ; python_version == "3.10"
|
|
26
|
+
Requires-Dist: sniffio (==1.3.1) ; python_version == "3.10"
|
|
27
|
+
Requires-Dist: starlette (==0.48.0) ; python_version == "3.10"
|
|
28
|
+
Requires-Dist: transitions (==0.9.3) ; python_version == "3.10"
|
|
29
|
+
Requires-Dist: typing-extensions (==4.15.0) ; python_version == "3.10"
|
|
30
|
+
Requires-Dist: typing-inspection (==0.4.2) ; python_version == "3.10"
|
|
31
|
+
Requires-Dist: uvicorn (==0.35.0) ; python_version == "3.10"
|
|
32
|
+
Requires-Dist: vention-communication (==0.3.0) ; python_version == "3.10"
|
|
17
33
|
Description-Content-Type: text/markdown
|
|
18
34
|
|
|
19
35
|
# vention-state-machine
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
state_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
state_machine/core.py,sha256=w0dmNx2MUCGSwVOs6Y9dGiHuRVNn7lWYskYtm1zpo4g,11007
|
|
3
|
+
state_machine/decorator_manager.py,sha256=rKyDKFMh-NmOrYntVwQNslSmW5Ac85BPzo0CXwe2Ixs,4336
|
|
4
|
+
state_machine/decorator_protocols.py,sha256=E6_xflTPxxTqDBa4Dj4p3OdPvR_EW0jA3PAjHTDlG8c,485
|
|
5
|
+
state_machine/decorators.py,sha256=mqlbbZ59d-eeRCmidbkFKGcjN4KPlF7l0Z6nEJwvt_Y,3598
|
|
6
|
+
state_machine/defs.py,sha256=RrM5SVgptfPZNu-AeRN56lk8QVRT1Uwot2xCx3SJpPo,2822
|
|
7
|
+
state_machine/machine_protocols.py,sha256=Q_rjlFTGrf3EvCndY4SWfurFNdJg4hB3qZkM0ISQxXI,784
|
|
8
|
+
state_machine/utils.py,sha256=1b54DZPv7s6hK0ud2KT4k1s1R1Li7wFUowKGTwMwxfw,2361
|
|
9
|
+
state_machine/vention_communication.py,sha256=Ggg4FvQzruHfpNJmPtDmdZM46w0JwH-BATDtsCS6T_I,5460
|
|
10
|
+
vention_state_machine-0.4.2.dist-info/METADATA,sha256=kb6qjgj-Ckjr8eBgn71n5AAqUuiUSvy3iY4vX68_A4k,12827
|
|
11
|
+
vention_state_machine-0.4.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
12
|
+
vention_state_machine-0.4.2.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
state_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
state_machine/core.py,sha256=lAiGGPtWCiYQPCh1iz3P26YFrz8VvveIGlL5k2AFvKs,11201
|
|
3
|
-
state_machine/decorator_manager.py,sha256=uHhbtR1vS7idcn4kJsaNtJ1Z_7kiHf9YVa_cxkb-WNk,4420
|
|
4
|
-
state_machine/decorator_protocols.py,sha256=E6_xflTPxxTqDBa4Dj4p3OdPvR_EW0jA3PAjHTDlG8c,485
|
|
5
|
-
state_machine/decorators.py,sha256=V-KBJ6fSvINi7JHN_VmrQLTnOxGPy5iRV5QzXLKwWDw,3668
|
|
6
|
-
state_machine/defs.py,sha256=eYtkTqRMm89ZHFKs3p7H3HyGNZbrOnoCzAJbSeMxSDg,2836
|
|
7
|
-
state_machine/machine_protocols.py,sha256=Yxs4aw2-NJ1RluygbthsquVKn35-xMYae7PB7xlqQmE,826
|
|
8
|
-
state_machine/utils.py,sha256=3eWL63_RAk_Jijh0ZIArBsUF3A7f0vQmwTjDRwRvpIs,2395
|
|
9
|
-
state_machine/vention_communication.py,sha256=BZjHC1SsXFqLiSpX0ChCEGfhkEeitpXmLClZCQIgOrA,5530
|
|
10
|
-
vention_state_machine-0.4.0.dist-info/METADATA,sha256=UlROQZDnqaO5j0AbfJJ2KofpSrrr_woWR8xPWbJY35c,11671
|
|
11
|
-
vention_state_machine-0.4.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
12
|
-
vention_state_machine-0.4.0.dist-info/RECORD,,
|
|
File without changes
|