Merge "Allow providing and using a 'nested_start_state_fetcher'"
This commit is contained in:
commit
fdfb1e0b3c
|
@ -107,6 +107,7 @@ class FiniteMachine(object):
|
|||
|
||||
@property
|
||||
def current_state(self):
|
||||
"""The current state the machine is in (or none if not initialized)."""
|
||||
if self._current is not None:
|
||||
return self._current.name
|
||||
return None
|
||||
|
@ -121,11 +122,11 @@ class FiniteMachine(object):
|
|||
def add_state(self, state, terminal=False, on_enter=None, on_exit=None):
|
||||
"""Adds a given state to the state machine.
|
||||
|
||||
The on_enter and on_exit callbacks, if provided will be expected to
|
||||
take two positional parameters, these being the state being exited (for
|
||||
on_exit) or the state being entered (for on_enter) and a second
|
||||
parameter which is the event that is being processed that caused the
|
||||
state transition.
|
||||
The ``on_enter`` and ``on_exit`` callbacks, if provided will be
|
||||
expected to take two positional parameters, these being the state
|
||||
being exited (for ``on_exit``) or the state being entered (for
|
||||
``on_enter``) and a second parameter which is the event that is
|
||||
being processed that caused the state transition.
|
||||
"""
|
||||
if self.frozen:
|
||||
raise excp.FrozenMachine()
|
||||
|
@ -249,7 +250,13 @@ class FiniteMachine(object):
|
|||
return self._post_process_event(event, result)
|
||||
|
||||
def initialize(self, start_state=None):
|
||||
"""Sets up the state machine (sets current state to start state...)."""
|
||||
"""Sets up the state machine (sets current state to start state...).
|
||||
|
||||
:param start_state: explicit start state to use to initialize the
|
||||
state machine to. If ``None`` is provided then
|
||||
the machine's default start state will be used
|
||||
instead.
|
||||
"""
|
||||
if start_state is None:
|
||||
start_state = self._default_start_state
|
||||
if start_state not in self._states:
|
||||
|
@ -391,6 +398,15 @@ class HierarchicalFiniteMachine(FiniteMachine):
|
|||
|
||||
def add_state(self, state,
|
||||
terminal=False, on_enter=None, on_exit=None, machine=None):
|
||||
"""Adds a given state to the state machine.
|
||||
|
||||
:param machine: the nested state machine that will be transitioned
|
||||
into when this state is entered
|
||||
:type machine: :py:class:`.FiniteMachine`
|
||||
|
||||
Further arguments are interpreted as
|
||||
for :py:meth:`.FiniteMachine.add_state`.
|
||||
"""
|
||||
if machine is not None and not isinstance(machine, FiniteMachine):
|
||||
raise ValueError(
|
||||
"Nested state machines must themselves be state machines")
|
||||
|
@ -409,13 +425,45 @@ class HierarchicalFiniteMachine(FiniteMachine):
|
|||
c._nested_machines = self._nested_machines.copy()
|
||||
return c
|
||||
|
||||
def initialize(self, start_state=None):
|
||||
def initialize(self, start_state=None,
|
||||
nested_start_state_fetcher=None):
|
||||
"""Sets up the state machine (sets current state to start state...).
|
||||
|
||||
:param start_state: explicit start state to use to initialize the
|
||||
state machine to. If ``None`` is provided then the
|
||||
machine's default start state will be used
|
||||
instead.
|
||||
:param nested_start_state_fetcher: A callback that can return start
|
||||
states for any nested machines
|
||||
**only**. If not ``None`` then it
|
||||
will be provided a single argument,
|
||||
the machine to provide a starting
|
||||
state for and it is expected to
|
||||
return a starting state (or
|
||||
``None``) for each machine called
|
||||
with. Do note that this callback
|
||||
will also be passed to other nested
|
||||
state machines as well, so it will
|
||||
also be used to initialize any state
|
||||
machines they contain (recursively).
|
||||
"""
|
||||
super(HierarchicalFiniteMachine, self).initialize(
|
||||
start_state=start_state)
|
||||
for data in six.itervalues(self._states):
|
||||
if 'machine' in data:
|
||||
data['machine'].initialize()
|
||||
nested_machine = data['machine']
|
||||
nested_start_state = None
|
||||
if nested_start_state_fetcher is not None:
|
||||
nested_start_state = nested_start_state_fetcher(
|
||||
nested_machine)
|
||||
if isinstance(nested_machine, HierarchicalFiniteMachine):
|
||||
nested_machine.initialize(
|
||||
start_state=nested_start_state,
|
||||
nested_start_state_fetcher=nested_start_state_fetcher)
|
||||
else:
|
||||
nested_machine.initialize(start_state=nested_start_state)
|
||||
|
||||
@property
|
||||
def nested_machines(self):
|
||||
"""Dictionary of **all** nested state machines this machine may use."""
|
||||
return self._nested_machines
|
||||
|
|
|
@ -328,6 +328,17 @@ class HFSMTest(FSMTest):
|
|||
dialer, _number_calling = self._make_phone_dialer()
|
||||
self.assertEqual(1, len(dialer.nested_machines))
|
||||
|
||||
def test_nested_machine_initializers(self):
|
||||
dialer, _number_calling = self._make_phone_dialer()
|
||||
queried_for = []
|
||||
|
||||
def init_with(nested_machine):
|
||||
queried_for.append(nested_machine)
|
||||
return None
|
||||
|
||||
dialer.initialize(nested_start_state_fetcher=init_with)
|
||||
self.assertEqual(1, len(queried_for))
|
||||
|
||||
def test_phone_dialer_iter(self):
|
||||
dialer, number_calling = self._make_phone_dialer()
|
||||
self.assertEqual(0, len(number_calling))
|
||||
|
|
|
@ -24,6 +24,7 @@ extensions = [
|
|||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.inheritance_diagram',
|
||||
'sphinx.ext.viewcode',
|
||||
'oslosphinx',
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue