tinyfsm 0.0.2__tar.gz → 0.1.0__tar.gz
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.
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/PKG-INFO +1 -1
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/pyproject.toml +1 -1
- tinyfsm-0.1.0/tinyfsm/__init__.py +1 -0
- tinyfsm-0.1.0/tinyfsm/exc.py +107 -0
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/tinyfsm/runner.py +9 -4
- tinyfsm-0.0.2/tinyfsm/__init__.py +0 -1
- tinyfsm-0.0.2/tinyfsm/exc.py +0 -60
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/LICENSE.txt +0 -0
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/README.md +0 -0
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/tinyfsm/_export_list.py +0 -0
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/tinyfsm/api.py +0 -0
- {tinyfsm-0.0.2 → tinyfsm-0.1.0}/tinyfsm/interface.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from typing import Any, Sequence
|
|
2
|
+
|
|
3
|
+
from . import _export_list
|
|
4
|
+
|
|
5
|
+
__all__ = export = _export_list.ExportList() # type: ignore
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@export
|
|
9
|
+
class TinyFSMError(Exception):
|
|
10
|
+
"""Common base class for all exception this library may raise."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@export
|
|
14
|
+
class InconsistentDefinitionError(TinyFSMError):
|
|
15
|
+
"""Raised when state machine definition is found to be inconsistent.
|
|
16
|
+
|
|
17
|
+
This means that no traversal was defined for the current state, i.e. there
|
|
18
|
+
is no next state to move to, no matter what the input will be.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
#: The rejected input object.
|
|
22
|
+
input: Any
|
|
23
|
+
|
|
24
|
+
#: The current state name.
|
|
25
|
+
current_state: str
|
|
26
|
+
|
|
27
|
+
def __init__(self, input: Any, current_state: str):
|
|
28
|
+
super().__init__()
|
|
29
|
+
self.input = input
|
|
30
|
+
self.current_state = current_state
|
|
31
|
+
|
|
32
|
+
def __str__(self) -> str:
|
|
33
|
+
return f"input {self.input!r} was rejected; no next state defined for the current state {self.current_state!r}"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@export
|
|
37
|
+
class InputRejectedError(TinyFSMError):
|
|
38
|
+
"""Raised when input was rejected by the state machine.
|
|
39
|
+
|
|
40
|
+
Input is rejected when it is not possible to deterministically choose the
|
|
41
|
+
next state for the current input.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
#: The rejected input object.
|
|
45
|
+
input: Any
|
|
46
|
+
|
|
47
|
+
#: The current state name.
|
|
48
|
+
current_state: str
|
|
49
|
+
|
|
50
|
+
#: The candidate state names.
|
|
51
|
+
#:
|
|
52
|
+
#: These are names of the states that are accessible from the current state
|
|
53
|
+
#: given via :attr:`current_state`.
|
|
54
|
+
next_state_candidates: Sequence[str]
|
|
55
|
+
|
|
56
|
+
def __init__(self, input: Any, current_state: str, next_state_candidates: Sequence[str]):
|
|
57
|
+
super().__init__()
|
|
58
|
+
self.input = input
|
|
59
|
+
self.current_state = current_state
|
|
60
|
+
self.next_state_candidates = next_state_candidates
|
|
61
|
+
|
|
62
|
+
def __str__(self) -> str:
|
|
63
|
+
return (
|
|
64
|
+
f"input {self.input!r} was rejected; "
|
|
65
|
+
f"cannot traverse from {self.current_state!r} to any of: "
|
|
66
|
+
f"{', '.join(repr(x) for x in self.next_state_candidates)}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@export
|
|
71
|
+
class FinalStateNotReached(TinyFSMError):
|
|
72
|
+
"""Raised when closing state machine if final state was not reached.
|
|
73
|
+
|
|
74
|
+
This usually means that either the input sequence is incomplete and more
|
|
75
|
+
data is required to reach the final state. For example, a final state
|
|
76
|
+
requires text to end with a period, but a period is missing and no more
|
|
77
|
+
characters are fed into the state machine.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
#: The last dispatched input.
|
|
81
|
+
last_input: Any
|
|
82
|
+
|
|
83
|
+
#: The name of the final state.
|
|
84
|
+
final_state: str
|
|
85
|
+
|
|
86
|
+
#: The name of the current state.
|
|
87
|
+
current_state: str
|
|
88
|
+
|
|
89
|
+
#: The candidate state names.
|
|
90
|
+
#:
|
|
91
|
+
#: These are names of the states that are accessible from the current state
|
|
92
|
+
#: given via :attr:`current_state`.
|
|
93
|
+
next_state_candidates: Sequence[str]
|
|
94
|
+
|
|
95
|
+
def __init__(self, last_input: Any, final_state: str, current_state: str, next_state_candidates: Sequence[str]):
|
|
96
|
+
super().__init__()
|
|
97
|
+
self.last_input = last_input
|
|
98
|
+
self.final_state = final_state
|
|
99
|
+
self.current_state = current_state
|
|
100
|
+
self.next_state_candidates = next_state_candidates
|
|
101
|
+
|
|
102
|
+
def __str__(self) -> str:
|
|
103
|
+
return (
|
|
104
|
+
f"final state {self.final_state!r} was not reached for last input {self.last_input!r}; "
|
|
105
|
+
f"more input data is expected to traverse from {self.current_state!r} "
|
|
106
|
+
f"to any of: {', '.join(repr(x) for x in self.next_state_candidates)}"
|
|
107
|
+
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Generic, Optional, Sequence, TypeVar
|
|
2
2
|
|
|
3
3
|
from . import _export_list
|
|
4
|
-
from .exc import InputRejectedError, FinalStateNotReached
|
|
4
|
+
from .exc import InconsistentDefinitionError, InputRejectedError, FinalStateNotReached
|
|
5
5
|
from .interface import StateMachineListener, Traversal
|
|
6
6
|
|
|
7
7
|
__all__ = export = _export_list.ExportList() # type: ignore
|
|
@@ -102,7 +102,7 @@ class StateMachineRunner(Generic[T]):
|
|
|
102
102
|
self.__last_input = input
|
|
103
103
|
current_state_traversals = self.__traversal_map.get(self.__current_state)
|
|
104
104
|
if current_state_traversals is None:
|
|
105
|
-
raise
|
|
105
|
+
raise InconsistentDefinitionError(input, self.__current_state)
|
|
106
106
|
for traversal in current_state_traversals:
|
|
107
107
|
if traversal.traverse_func(input):
|
|
108
108
|
next_state = traversal.target_state
|
|
@@ -110,7 +110,8 @@ class StateMachineRunner(Generic[T]):
|
|
|
110
110
|
self.__current_state = next_state
|
|
111
111
|
break
|
|
112
112
|
else:
|
|
113
|
-
|
|
113
|
+
next_state_candidates = tuple(x.target_state for x in current_state_traversals)
|
|
114
|
+
raise InputRejectedError(input, self.__current_state, next_state_candidates)
|
|
114
115
|
self.__listener.on_dispatch_done(input, self.__current_state)
|
|
115
116
|
|
|
116
117
|
def close(self):
|
|
@@ -122,7 +123,11 @@ class StateMachineRunner(Generic[T]):
|
|
|
122
123
|
:exc:`tinyfsm.exc.FinalStateNotReached` error.
|
|
123
124
|
"""
|
|
124
125
|
if not self.is_final():
|
|
125
|
-
|
|
126
|
+
current_state_traversals = self.__traversal_map.get(self.__current_state)
|
|
127
|
+
next_state_candidates = tuple(x.target_state for x in (current_state_traversals or []))
|
|
128
|
+
raise FinalStateNotReached(
|
|
129
|
+
self.__last_input, self.__final_state, self.__current_state, next_state_candidates
|
|
130
|
+
)
|
|
126
131
|
|
|
127
132
|
def is_final(self) -> bool:
|
|
128
133
|
"""Check if the final state is reached."""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.2"
|
tinyfsm-0.0.2/tinyfsm/exc.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from . import _export_list
|
|
4
|
-
|
|
5
|
-
__all__ = export = _export_list.ExportList() # type: ignore
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@export
|
|
9
|
-
class TinyFSMError(Exception):
|
|
10
|
-
"""Common base class for all exception this library may raise."""
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@export
|
|
14
|
-
class InputRejectedError(TinyFSMError):
|
|
15
|
-
"""Raised when input was rejected by the state machine.
|
|
16
|
-
|
|
17
|
-
Input is rejected only if there was no matching traversal found or if all
|
|
18
|
-
traversal functions returned ``False`` for that input.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
#: The rejected input object.
|
|
22
|
-
input: Any
|
|
23
|
-
|
|
24
|
-
#: The current state name.
|
|
25
|
-
current_state: str
|
|
26
|
-
|
|
27
|
-
def __init__(self, input: Any, current_state: str):
|
|
28
|
-
super().__init__()
|
|
29
|
-
self.input = input
|
|
30
|
-
self.current_state = current_state
|
|
31
|
-
|
|
32
|
-
def __str__(self) -> str:
|
|
33
|
-
return f"input {self.input!r} was rejected; no traversal found for current state {self.current_state!r}"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@export
|
|
37
|
-
class FinalStateNotReached(TinyFSMError):
|
|
38
|
-
"""Raised when closing state machine if final state was not reached.
|
|
39
|
-
|
|
40
|
-
This means that either the input sequence is incomplete, or that the state
|
|
41
|
-
machine definition is malformed and final state is unreachable.
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
#: The last dispatched input.
|
|
45
|
-
last_input: Any
|
|
46
|
-
|
|
47
|
-
#: The name of the final state.
|
|
48
|
-
final_state: str
|
|
49
|
-
|
|
50
|
-
#: The name of the current state.
|
|
51
|
-
current_state: str
|
|
52
|
-
|
|
53
|
-
def __init__(self, last_input: Any, final_state: str, current_state: str):
|
|
54
|
-
super().__init__()
|
|
55
|
-
self.last_input = last_input
|
|
56
|
-
self.final_state = final_state
|
|
57
|
-
self.current_state = current_state
|
|
58
|
-
|
|
59
|
-
def __str__(self) -> str:
|
|
60
|
-
return f"final state {self.final_state!r} was not reached; current state is {self.current_state!r}, last input was {self.last_input!r}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|