Use constants for runner state machine event names

Instead of using strings it is better if we can use constants (that
may be the same/adjusted strings) and use those instead in the state
machine used in the runner.

The names are adjusted (and the state graph diagram and docstring)
to reflect names that fit better with there intended meaning and
usage.

Change-Id: Iaf229d6e37730545ba9f2708d118697cb7145992
This commit is contained in:
Joshua Harlow 2015-01-15 14:52:20 -08:00 committed by Joshua Harlow
parent 4561710908
commit 1ed0f22fd3
3 changed files with 67 additions and 49 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -27,6 +27,17 @@ _UNDEFINED = 'UNDEFINED'
_GAME_OVER = 'GAME_OVER'
_META_STATES = (_GAME_OVER, _UNDEFINED)
# Event name constants the state machine uses.
_SCHEDULE = 'schedule_next'
_WAIT = 'wait_finished'
_ANALYZE = 'examine_finished'
_FINISH = 'completed'
_FAILED = 'failed'
_SUSPENDED = 'suspended'
_SUCCESS = 'success'
_REVERTED = 'reverted'
_START = 'start'
LOG = logging.getLogger(__name__)
@ -45,25 +56,25 @@ class _MachineBuilder(object):
NOTE(harlowja): the machine states that this build will for are::
+--------------+-----------+------------+----------+---------+
Start | Event | End | On Enter | On Exit
+--------------+-----------+------------+----------+---------+
ANALYZING | finished | GAME_OVER | |
ANALYZING | schedule | SCHEDULING | |
ANALYZING | wait | WAITING | |
FAILURE[$] | | | |
GAME_OVER | failed | FAILURE | |
GAME_OVER | reverted | REVERTED | |
GAME_OVER | success | SUCCESS | |
GAME_OVER | suspended | SUSPENDED | |
RESUMING | schedule | SCHEDULING | |
REVERTED[$] | | | |
SCHEDULING | wait | WAITING | |
SUCCESS[$] | | | |
SUSPENDED[$] | | | |
UNDEFINED[^] | start | RESUMING | |
WAITING | analyze | ANALYZING | |
+--------------+-----------+------------+----------+---------+
+--------------+------------------+------------+----------+---------+
Start | Event | End | On Enter | On Exit
+--------------+------------------+------------+----------+---------+
ANALYZING | completed | GAME_OVER | |
ANALYZING | schedule_next | SCHEDULING | |
ANALYZING | wait_finished | WAITING | |
FAILURE[$] | | | |
GAME_OVER | failed | FAILURE | |
GAME_OVER | reverted | REVERTED | |
GAME_OVER | success | SUCCESS | |
GAME_OVER | suspended | SUSPENDED | |
RESUMING | schedule_next | SCHEDULING | |
REVERTED[$] | | | |
SCHEDULING | wait_finished | WAITING | |
SUCCESS[$] | | | |
SUSPENDED[$] | | | |
UNDEFINED[^] | start | RESUMING | |
WAITING | examine_finished | ANALYZING | |
+--------------+------------------+------------+----------+---------+
Between any of these yielded states (minus ``GAME_OVER`` and ``UNDEFINED``)
if the engine has been suspended or the engine has failed (due to a
@ -90,17 +101,17 @@ class _MachineBuilder(object):
def resume(old_state, new_state, event):
memory.next_nodes.update(self._completer.resume())
memory.next_nodes.update(self._analyzer.get_next_nodes())
return 'schedule'
return _SCHEDULE
def game_over(old_state, new_state, event):
if memory.failures:
return 'failed'
return _FAILED
if self._analyzer.get_next_nodes():
return 'suspended'
return _SUSPENDED
elif self._analyzer.is_success():
return 'success'
return _SUCCESS
else:
return 'reverted'
return _REVERTED
def schedule(old_state, new_state, event):
if self.runnable() and memory.next_nodes:
@ -111,7 +122,7 @@ class _MachineBuilder(object):
if failures:
memory.failures.extend(failures)
memory.next_nodes.clear()
return 'wait'
return _WAIT
def wait(old_state, new_state, event):
# TODO(harlowja): maybe we should start doing 'yield from' this
@ -122,7 +133,7 @@ class _MachineBuilder(object):
timeout)
memory.done.update(done)
memory.not_done = not_done
return 'analyze'
return _ANALYZE
def analyze(old_state, new_state, event):
next_nodes = set()
@ -145,11 +156,11 @@ class _MachineBuilder(object):
next_nodes.update(more_nodes)
if self.runnable() and next_nodes and not memory.failures:
memory.next_nodes.update(next_nodes)
return 'schedule'
return _SCHEDULE
elif memory.not_done:
return 'wait'
return _WAIT
else:
return 'finished'
return _FINISH
def on_exit(old_state, event):
LOG.debug("Exiting old state '%s' in response to event '%s'",
@ -178,23 +189,23 @@ class _MachineBuilder(object):
m.add_state(st.WAITING, **watchers)
m.add_state(st.FAILURE, terminal=True, **watchers)
m.add_transition(_GAME_OVER, st.REVERTED, 'reverted')
m.add_transition(_GAME_OVER, st.SUCCESS, 'success')
m.add_transition(_GAME_OVER, st.SUSPENDED, 'suspended')
m.add_transition(_GAME_OVER, st.FAILURE, 'failed')
m.add_transition(_UNDEFINED, st.RESUMING, 'start')
m.add_transition(st.ANALYZING, _GAME_OVER, 'finished')
m.add_transition(st.ANALYZING, st.SCHEDULING, 'schedule')
m.add_transition(st.ANALYZING, st.WAITING, 'wait')
m.add_transition(st.RESUMING, st.SCHEDULING, 'schedule')
m.add_transition(st.SCHEDULING, st.WAITING, 'wait')
m.add_transition(st.WAITING, st.ANALYZING, 'analyze')
m.add_transition(_GAME_OVER, st.REVERTED, _REVERTED)
m.add_transition(_GAME_OVER, st.SUCCESS, _SUCCESS)
m.add_transition(_GAME_OVER, st.SUSPENDED, _SUSPENDED)
m.add_transition(_GAME_OVER, st.FAILURE, _FAILED)
m.add_transition(_UNDEFINED, st.RESUMING, _START)
m.add_transition(st.ANALYZING, _GAME_OVER, _FINISH)
m.add_transition(st.ANALYZING, st.SCHEDULING, _SCHEDULE)
m.add_transition(st.ANALYZING, st.WAITING, _WAIT)
m.add_transition(st.RESUMING, st.SCHEDULING, _SCHEDULE)
m.add_transition(st.SCHEDULING, st.WAITING, _WAIT)
m.add_transition(st.WAITING, st.ANALYZING, _ANALYZE)
m.add_reaction(_GAME_OVER, 'finished', game_over)
m.add_reaction(st.ANALYZING, 'analyze', analyze)
m.add_reaction(st.RESUMING, 'start', resume)
m.add_reaction(st.SCHEDULING, 'schedule', schedule)
m.add_reaction(st.WAITING, 'wait', wait)
m.add_reaction(_GAME_OVER, _FINISH, game_over)
m.add_reaction(st.ANALYZING, _ANALYZE, analyze)
m.add_reaction(st.RESUMING, _START, resume)
m.add_reaction(st.SCHEDULING, _SCHEDULE, schedule)
m.add_reaction(st.WAITING, _WAIT, wait)
m.freeze()
return (m, memory)
@ -231,7 +242,7 @@ class Runner(object):
def run_iter(self, timeout=None):
"""Runs the nodes using a built state machine."""
machine, memory = self.builder.build(timeout=timeout)
for (_prior_state, new_state) in machine.run_iter('start'):
for (_prior_state, new_state) in machine.run_iter(_START):
# NOTE(harlowja): skip over meta-states.
if new_state not in _META_STATES:
if new_state == st.FAILURE:

View File

@ -41,6 +41,12 @@ class DummyRuntime(object):
self.storage = None
def clean_event(name):
name = name.replace("_", " ")
name = name.strip()
return name
def make_machine(start_state, transitions, disallowed):
machine = fsm.FSM(start_state)
machine.add_state(start_state)
@ -129,6 +135,7 @@ def main():
}
nodes = {}
for (start_state, on_event, end_state) in source:
on_event = clean_event(on_event)
if start_state not in nodes:
start_node_attrs = node_attrs.copy()
text_color = map_color(internal_states, start_state)
@ -145,7 +152,7 @@ def main():
g.add_node(nodes[end_state])
if options.engines:
edge_attrs = {
'label': "on %s" % on_event
'label': on_event,
}
if 'reverted' in on_event:
edge_attrs['fontcolor'] = 'darkorange'