Beispiel #1
0
 def test_run_send_fail(self):
     up_downs = []
     runner = runners.FiniteRunner(self.jumper)
     it = runner.run_iter('jump')
     up_downs.append(six.next(it))
     self.assertRaises(excp.NotFound, it.send, 'fail')
     it.close()
     self.assertEqual([('down', 'up')], up_downs)
Beispiel #2
0
    def test_copy_initialized(self):
        j = self.jumper.copy()
        self.assertIsNone(j.current_state)
        r = runners.FiniteRunner(self.jumper)

        for i, transition in enumerate(r.run_iter('jump')):
            if i == 4:
                break

        self.assertIsNone(j.current_state)
        self.assertIsNotNone(self.jumper.current_state)
Beispiel #3
0
 def test_run_iter(self):
     up_downs = []
     runner = runners.FiniteRunner(self.jumper)
     for (old_state, new_state) in runner.run_iter('jump'):
         up_downs.append((old_state, new_state))
         if len(up_downs) >= 3:
             break
     self.assertEqual([('down', 'up'), ('up', 'down'), ('down', 'up')],
                      up_downs)
     self.assertFalse(self.jumper.terminated)
     self.assertEqual('up', self.jumper.current_state)
     self.jumper.process_event('fall')
     self.assertEqual('down', self.jumper.current_state)
Beispiel #4
0
 def test_run_send(self):
     up_downs = []
     runner = runners.FiniteRunner(self.jumper)
     it = runner.run_iter('jump')
     while True:
         up_downs.append(it.send(None))
         if len(up_downs) >= 3:
             it.close()
             break
     self.assertEqual('up', self.jumper.current_state)
     self.assertFalse(self.jumper.terminated)
     self.assertEqual([('down', 'up'), ('up', 'down'), ('down', 'up')],
                      up_downs)
     self.assertRaises(StopIteration, six.next, it)
Beispiel #5
0
 def test_run(self):
     m = self._create_fsm('down', add_states=['up', 'down'])
     m.add_state('broken', terminal=True)
     m.add_transition('down', 'up', 'jump')
     m.add_transition('up', 'broken', 'hit-wall')
     m.add_reaction('up', 'jump', lambda *args: 'hit-wall')
     self.assertEqual(['broken', 'down', 'up'], sorted(m.states))
     self.assertEqual(2, m.events)
     m.initialize()
     self.assertEqual('down', m.current_state)
     self.assertFalse(m.terminated)
     r = runners.FiniteRunner(m)
     r.run('jump')
     self.assertTrue(m.terminated)
     self.assertEqual('broken', m.current_state)
     self.assertRaises(excp.InvalidState, r.run, 'jump', initialize=False)
Beispiel #6
0
    def run_iter(self, timeout=None):
        """Runs the engine using iteration (or die trying).

        :param timeout: timeout to wait for any atoms to complete (this timeout
            will be used during the waiting period that occurs after the
            waiting state is yielded when unfinished atoms are being waited
            on).

        Instead of running to completion in a blocking manner, this will
        return a generator which will yield back the various states that the
        engine is going through (and can be used to run multiple engines at
        once using a generator per engine). The iterator returned also
        responds to the ``send()`` method from :pep:`0342` and will attempt to
        suspend itself if a truthy value is sent in (the suspend may be
        delayed until all active atoms have finished).

        NOTE(harlowja): using the ``run_iter`` method will **not** retain the
        engine lock while executing so the user should ensure that there is
        only one entity using a returned engine iterator (one per engine) at a
        given time.
        """
        self.compile()
        self.prepare()
        self.validate()
        last_state = None
        with _start_stop(self._task_executor, self._retry_executor):
            self._change_state(states.RUNNING)
            try:
                closed = False
                machine, memory = self._runtime.builder.build(timeout=timeout)
                r = runners.FiniteRunner(machine)
                for (_prior_state, new_state) in r.run_iter(builder.START):
                    last_state = new_state
                    # NOTE(harlowja): skip over meta-states.
                    if new_state in builder.META_STATES:
                        continue
                    if new_state == states.FAILURE:
                        failure.Failure.reraise_if_any(memory.failures)
                    if closed:
                        continue
                    try:
                        try_suspend = yield new_state
                    except GeneratorExit:
                        # The generator was closed, attempt to suspend and
                        # continue looping until we have cleanly closed up
                        # shop...
                        closed = True
                        self.suspend()
                    else:
                        if try_suspend:
                            self.suspend()
            except Exception:
                with excutils.save_and_reraise_exception():
                    self._change_state(states.FAILURE)
            else:
                if last_state and last_state not in self.IGNORABLE_STATES:
                    self._change_state(new_state)
                    if last_state not in self.NO_RERAISING_STATES:
                        it = itertools.chain(
                            six.itervalues(self.storage.get_failures()),
                            six.itervalues(self.storage.get_revert_failures()))
                        failure.Failure.reraise_if_any(it)
Beispiel #7
0
    def run_iter(self, timeout=None):
        """Runs the engine using iteration (or die trying).

        :param timeout: timeout to wait for any atoms to complete (this timeout
            will be used during the waiting period that occurs after the
            waiting state is yielded when unfinished atoms are being waited
            on).

        Instead of running to completion in a blocking manner, this will
        return a generator which will yield back the various states that the
        engine is going through (and can be used to run multiple engines at
        once using a generator per engine). The iterator returned also
        responds to the ``send()`` method from :pep:`0342` and will attempt to
        suspend itself if a truthy value is sent in (the suspend may be
        delayed until all active atoms have finished).

        NOTE(harlowja): using the ``run_iter`` method will **not** retain the
        engine lock while executing so the user should ensure that there is
        only one entity using a returned engine iterator (one per engine) at a
        given time.
        """
        self.compile()
        self.prepare()
        self.validate()
        # Keep track of the last X state changes, which if a failure happens
        # are quite useful to log (and the performance of tracking this
        # should be negligible).
        last_transitions = collections.deque(
            maxlen=max(1, self.MAX_MACHINE_STATES_RETAINED))
        with _start_stop(self._task_executor, self._retry_executor):
            self._change_state(states.RUNNING)
            try:
                closed = False
                machine, memory = self._runtime.builder.build(timeout=timeout)
                r = runners.FiniteRunner(machine)
                for transition in r.run_iter(builder.START):
                    last_transitions.append(transition)
                    _prior_state, new_state = transition
                    # NOTE(harlowja): skip over meta-states
                    if new_state in builder.META_STATES:
                        continue
                    if new_state == states.FAILURE:
                        failure.Failure.reraise_if_any(memory.failures)
                    if closed:
                        continue
                    try:
                        try_suspend = yield new_state
                    except GeneratorExit:
                        # The generator was closed, attempt to suspend and
                        # continue looping until we have cleanly closed up
                        # shop...
                        closed = True
                        self.suspend()
                    else:
                        if try_suspend:
                            self.suspend()
            except Exception:
                with excutils.save_and_reraise_exception():
                    LOG.exception(
                        "Engine execution has failed, something"
                        " bad must of happened (last"
                        " %s machine transitions were %s)",
                        last_transitions.maxlen, list(last_transitions))
                    self._change_state(states.FAILURE)
            else:
                if last_transitions:
                    _prior_state, new_state = last_transitions[-1]
                    if new_state not in self.IGNORABLE_STATES:
                        self._change_state(new_state)
                        if new_state not in self.NO_RERAISING_STATES:
                            failures = self.storage.get_failures()
                            more_failures = self.storage.get_revert_failures()
                            fails = itertools.chain(
                                six.itervalues(failures),
                                six.itervalues(more_failures))
                            failure.Failure.reraise_if_any(fails)
Beispiel #8
0
 def run(self, action):
     self.runner = runners.FiniteRunner(self.fsm)
     for (old, new) in self.runner.run_iter(action):
         print(old, '-->', new)
         self.show()
Beispiel #9
0
 def test_bad_start_state(self):
     m = self._create_fsm('unknown', add_start=False)
     r = runners.FiniteRunner(m)
     self.assertRaises(excp.NotFound, r.run, 'unknown')
Beispiel #10
0
 def _make_machine(self, flow, initial_state=None):
     runtime = self._make_runtime(flow, initial_state=initial_state)
     machine, memory = runtime.builder.build({})
     machine_runner = runners.FiniteRunner(machine)
     return (runtime, machine, memory, machine_runner)