-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstatemachine.py
executable file
·122 lines (107 loc) · 3.95 KB
/
statemachine.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/python
import networkx as nx
class StateMachineError(Exception):
pass
class State(object):
""" a state in a finite state machine
A state has a name
>>> s = State("start")
>>> s.name
'start'
>>> s.name == "fart"
False
>>> t = State()
Traceback (most recent call last):
...
StateMachineError: a State needs a name given as parameter
>>> t = State("")
Traceback (most recent call last):
...
StateMachineError: a State needs a name given as parameter
"""
def __init__(self,name=None):
if not name:
raise StateMachineError("a State needs a name given as parameter")
self.name = name
class StateMachine(nx.DiGraph):
""" A basic, somewhat ad-hoc finite state machine
>>> sm = StateMachine()
>>> welcomeState = State("welcome")
>>> loginState = State("login")
>>> annotateState = State("annotate")
>>> endState = State("end")
>>> sm.addTransition(welcomeState,loginState,
... test=lambda x: True if x=="agree to participate" else False,
... function=lambda x: "great, let's log you in")
>>> sm.addTransition(loginState,annotateState,
... test=lambda x: True if x=="correct login info" else False,
... function=lambda x: "great, let's start annotating")
>>> sm.addTransition(annotateState,annotateState,
... test=lambda x: True if x=="keep annotating" else False,
... function=lambda x: "great, keep going")
>>> sm.addTransition(annotateState,endState,
... test=lambda x: True if x=="done" else False,
... function=lambda x: "great, thanks")
>>> sm("test")
Traceback (most recent call last):
...
AttributeError: 'StateMachine' object has no attribute 'state'
oops, need to set the state
>>> sm.setState(welcomeState)
>>> sm("test")
Traceback (most recent call last):
...
StateMachineError: no valid transition using this input, given current state welcome
OK, just checkikng...
>>> sm("agree to participate")
"great, let's log you in"
>>> sm("correct login info")
"great, let's start annotating"
>>> sm("keep annotating")
'great, keep going'
>>> sm("keep annotating")
'great, keep going'
>>> sm("keep annotating")
'great, keep going'
and so on...
>>> sm("done")
'great, thanks'
>>> sm("done")
Traceback (most recent call last):
...
StateMachineError: no valid transition using this input, given current state end
no where left to go... except continue reading the code
"""
def __call__(self,input):
#catch special commands here
self.processGlobalCommands(input)
for n in self[self.state]: #check neighbor states
if self[self.state][n]['test'](input):
output = self[self.state][n]['function'](input)
self.setState(n)
return output
raise StateMachineError("no valid transition using this input, given current state %s"%self.state.name)
def processGlobalCommands(self,input):
pass
def __init__(self):
super(StateMachine,self).__init__()
def addState(self,s):
if not isinstance(s,State):
raise StateMachineError("only State objects can be added to a StateMachine")
self.add_node(s)
def addTransition(self,s1,s2,
test=lambda *args:True,
function=lambda *args:"",
weight=1.0):
if not isinstance(s1,State):
raise StateMachineError("transitions must connect two State objects")
if not isinstance(s2,State):
raise StateMachineError("transitions must connect two State objects")
self.add_edge(s1,s2,test=test,function=function)
def setState(self,s):
if s not in self.nodes():
raise StateMachineError("no such state")
self.state = s
if __name__ == "__main__":
import doctest
doctest.testmod()