예제 #1
0
def main() -> None:
    """Code here shows how everything is wired together.

    The following lines are the expected output:

        state 'state-a' got event 'None'
        state 'state-b' got event 'Event(name="to-state-b", data={})'
    """
    # Create the state machine -- the root context of every workflow.
    state_machine = Statechart("state-machine")

    # Create the workflow metadata with all the data accessible to all states and workflows.
    metadata = WorkflowMetadata(a=10, b="foo", c="bar")

    # Create the workflow factory backed by the workflow metadata.
    factory = WorkflowFactory(metadata)

    # Create the main workflow using the workflow factory.
    main_workflow = factory.create_workflow("main-workflow", state_machine, MainWorkflow)

    # Set up the main transitions.
    init = InitialState(state_machine)
    final = FinalState(state_machine)
    Transition(init, main_workflow)
    Transition(main_workflow, final, event=Event("final"))

    # Run the state machine.
    state_machine.start()
    state_machine.dispatch(Event("to-state-b"))
예제 #2
0
    def test_default_transition_from_finished_composite_state(self):
        statechart = Statechart(name='statechart')
        statechart_init = InitialState(statechart)

        composite_state = CompositeState(name='composite', context=statechart)
        comp_init = InitialState(composite_state)
        a = State(name='a', context=composite_state)
        comp_final = FinalState(composite_state)

        Transition(start=statechart_init, end=composite_state)
        Transition(start=comp_init, end=a)
        Transition(start=a, end=comp_final, event=Event('e'))

        b = State(name='b', context=statechart)
        c = State(name='c', context=statechart)
        d = State(name='d', context=statechart)

        Transition(start=composite_state, end=c, event=Event('f'))
        Transition(start=composite_state, end=b, event=Event('e'))
        Transition(start=composite_state, end=d)

        statechart.start()

        assert statechart.is_active('a')

        statechart.dispatch(Event('e'))

        assert statechart.is_active('d')
예제 #3
0
    def init(self, factory: WorkflowFactory) -> None:
        state_a = factory.create_state("state-a", self, StateA)
        state_b = factory.create_state("state-b", self, StateB)

        init = InitialState(self)
        final = FinalState(self)

        Transition(init, state_a)
        Transition(state_a, state_b, event=Event("to-state-b"))
        Transition(state_b, final, event=Event("final"))
예제 #4
0
    def test_transition_hierarchy(self, empty_statechart):
        sc = empty_statechart
        init = InitialState(sc)

        top = CompositeState(name='top', context=sc)
        top_init = InitialState(top)
        middle_a = CompositeState(name='middle_a', context=top)
        middle_b = CompositeState(name='middle_b', context=top)

        middle_a_init = InitialState(middle_a)
        bottom_a1 = State(name='bottom_a1', context=middle_a)
        bottom_a2 = State(name='bottom_a2', context=middle_a)

        middle_b_init = InitialState(middle_b)
        bottom_b1 = State(name='bottom_b1', context=middle_b)

        # Setup default transitions
        Transition(start=init, end=top)
        Transition(start=top_init, end=middle_a)
        Transition(start=middle_a_init, end=bottom_a1)
        Transition(start=middle_b_init, end=bottom_b1)

        # Setup event triggers
        across = Event('across')
        up = Event('up')

        # Setup external transitions
        Transition(start=bottom_a1, end=bottom_a2, event=across)
        Transition(start=bottom_a2, end=middle_b, event=up)
        Transition(start=middle_b, end=middle_a, event=across)

        sc.start()

        assert sc.is_active('top')
        assert sc.is_active('middle_a')
        assert sc.is_active('bottom_a1')

        sc.dispatch(across)

        assert sc.is_active('top')
        assert sc.is_active('middle_a')
        assert sc.is_active('bottom_a2')

        sc.dispatch(up)

        assert sc.is_active('top')
        assert sc.is_active('middle_b')
        assert sc.is_active('bottom_b1')

        sc.dispatch(across)

        assert sc.is_active('top')
        assert sc.is_active('middle_a')
        assert sc.is_active('bottom_a1')
예제 #5
0
    def test_event_data(self):
        name = 'ev'
        data = {'a': 1}
        ev = Event(name, data)

        assert ev.name == name
        assert ev.data == data
예제 #6
0
    def test_transition_from_initial_state_with_event_trigger(self):
        startchart = Statechart(name='statechart')
        initial_state = InitialState(startchart)
        default_state = State(name='default', context=startchart)

        with pytest.raises(RuntimeError):
            Transition(start=initial_state, end=default_state, event=Event('event'))
예제 #7
0
    def test_submachines(self):
        statechart = Statechart(name='statechart')

        init = InitialState(statechart)
        top_a = self.Submachine('top a', statechart)
        top_b = self.Submachine('top b', statechart)

        top_a_to_b = Event('top ab')
        Transition(start=init, end=top_a)
        Transition(start=top_a, end=top_b, event=top_a_to_b)

        statechart.start()

        assert statechart.is_active('top a')
        assert statechart.is_active('sub state a')

        statechart.dispatch(top_a.sub_a_to_b)

        assert statechart.is_active('top a')
        assert statechart.is_active('sub state b')

        statechart.dispatch(top_a_to_b)

        assert statechart.is_active('top b')
        assert statechart.is_active('sub state a')

        statechart.dispatch(top_a.sub_a_to_b)

        assert statechart.is_active('top b')
        assert statechart.is_active('sub state b')
예제 #8
0
def concurrent_statechart():
    def composite(name, context):
        composite = CompositeState(name=name, context=context)
        composite.init = InitialState(composite)
        composite.state = State(name='state', context=composite)
        composite.final = FinalState(composite)

        Transition(start=composite.init, end=composite.state)
        Transition(start=composite.state,
                   end=composite.final,
                   event=Event('finish'))

        return composite

    sc = Statechart('simple')
    sc.init = InitialState(sc)

    sc.concurrent = ConcurrentState(name='compound', context=sc)

    sc.concurrent.composite_a = composite(name='a', context=sc.concurrent)
    sc.concurrent.composite_b = composite(name='b', context=sc.concurrent)
    sc.concurrent.composite_c = composite(name='c', context=sc.concurrent)

    sc.final = FinalState(sc)

    Transition(start=sc.init, end=sc.concurrent)
    Transition(start=sc.concurrent, end=sc.final, event=Event('finish'))

    return sc
예제 #9
0
    def __init__(self, start, end, event=None, guard=None, action=None):
        self._logger = logging.getLogger(self.__class__.__name__)
        self.start = start
        self.end = end
        self.event = event
        self.guard = guard
        self.action = action

        if isinstance(event, str):
            self.event = Event(event)

        if guard is not None and not callable(guard):
            raise ValueError('Guard must be callable')

        if action is not None and not callable(action):
            raise ValueError('Action must be callable')

        """ Used to store the states that will get activated """
        self.activate = list()

        """ Used to store the states that will get de-activated """
        self.deactivate = list()

        self._calculate_state_set(start=start, end=end)

        start.add_transition(self)
예제 #10
0
    def test_execute(self, empty_statechart):
        initial_state = InitialState(empty_statechart)
        default_state = self.StateSpy(name='next', context=empty_statechart)
        Transition(start=initial_state, end=default_state)

        internal_event = Event(name='internal-event')
        internal_action = ActionSpy()
        InternalTransition(state=default_state,
                           event=internal_event,
                           action=internal_action)
        empty_statechart.start()

        assert empty_statechart.is_active('next')
        assert default_state.entry_executed is True
        assert default_state.do_executed is True
        assert default_state.exit_executed is False

        # Ensure we don't leave and re-enter the default state after triggering
        # the internal transition.
        default_state.entry_executed = False
        default_state.do_executed = False

        empty_statechart.dispatch(internal_event)

        assert default_state.entry_executed is False
        assert default_state.do_executed is False
        assert default_state.exit_executed is False

        assert internal_action.executed is True
예제 #11
0
    def test_transition_event_consumed(self, empty_statechart):
        sc = empty_statechart
        init = InitialState(sc)

        # a = State(name='a', context=sc)
        b = State(name='b', context=sc)

        cs = CompositeState(name='cs', context=sc)
        cs_init = InitialState(cs)
        cs_a = State(name='cs a', context=cs)
        cs_b = State(name='cs b', context=cs)

        Transition(start=init, end=cs)
        Transition(start=cs, end=cs_init)

        Transition(start=cs_init, end=cs_a)
        Transition(start=cs_a, end=cs_b, event='home')
        Transition(start=cs, end=b, event='home')

        sc.start()

        assert sc.is_active('cs a')

        sc.dispatch(Event('home'))

        assert sc.is_active('cs b')
예제 #12
0
    def test_composite_statechart_finished(self):
        statechart = Statechart(name='statechart')
        init = InitialState(statechart)
        final = FinalState(statechart)

        composite = CompositeState(name='composite', context=statechart)
        composite_init = InitialState(composite)
        composite_default = State(name='composite_default', context=composite)
        composite_final = FinalState(composite)

        finish = Event('finish')

        Transition(start=init, end=composite)
        Transition(start=composite_init, end=composite_default)
        Transition(start=composite_default, end=composite_final, event=finish)
        Transition(start=composite, end=final)

        statechart.start()

        assert statechart.is_active('composite')
        assert statechart.is_active('composite_default')
        assert not statechart.is_finished()

        statechart.dispatch(finish)

        assert statechart.is_finished()
예제 #13
0
    def test_light_switch(self):
        """
                      --Flick-->
        init ---> Off            On
                      <--Flick--   Entry: Light = ON
                                   Exit: Light = OFF
                                   Internal:
                                     Flick: Count++
        """
        class On(State):
            def __init__(self, name, context, data):
                State.__init__(self, name=name, context=context)
                self.data = data

            def entry(self, event):
                self.data['light'] = 'on'

            def exit(self, event):
                self.data['light'] = 'off'

            def handle_internal(self, event):
                if event.name == 'flick':
                    self.data['on_count'] += 1

        sm = Statechart(name='sm')
        data = dict(light='off', on_count=0)
        sm.initial_state = InitialState(context=sm)
        off = State(name='off', context=sm)
        on = On(name='on', context=sm, data=data)

        Transition(start=sm.initial_state, end=off)
        Transition(start=off, end=on, event=Event('flick'))
        Transition(start=on, end=off, event=Event('flick'))

        sm.start()

        assert data['light'] == 'off'

        sm.dispatch(Event('flick'))
        assert data['light'] == 'on'

        assert data['on_count'] == 0

        sm.dispatch(Event('flick'))
        assert data['light'] == 'off'

        assert data['on_count'] == 1
예제 #14
0
def simple_statechart():
    sc = Statechart('simple')
    sc.init = InitialState(sc)
    sc.state = State(name='state', context=sc)
    sc.final = FinalState(sc)

    Transition(start=sc.init, end=sc.state)
    Transition(start=sc.state, end=sc.final, event=Event('finish'))

    return sc
예제 #15
0
        def __init__(self, name, context):
            CompositeState.__init__(self, name=name, context=context)

            init = InitialState(self)
            self.state_a = State(name='sub state a', context=self)
            self.state_b = State(name='sub state b', context=self)

            self.sub_a_to_b = Event('sub_ab')
            Transition(start=init, end=self.state_a)
            Transition(start=self.state_a,
                       end=self.state_b,
                       event=self.sub_a_to_b)
예제 #16
0
    def composite(name, context):
        composite = CompositeState(name=name, context=context)
        composite.init = InitialState(composite)
        composite.state = State(name='state', context=composite)
        composite.final = FinalState(composite)

        Transition(start=composite.init, end=composite.state)
        Transition(start=composite.state,
                   end=composite.final,
                   event=Event('finish'))

        return composite
예제 #17
0
    def test_local_transition(self, empty_statechart):
        init = InitialState(empty_statechart)
        state_spy = self.StateSpy(name='spy', context=empty_statechart)

        Transition(start=init, end=state_spy)
        Transition(start=state_spy, end=state_spy.local, event=Event('local'))

        empty_statechart.start()

        assert empty_statechart.is_active('spy')
        assert empty_statechart.is_active('default')
        assert state_spy.entries is 1
        assert state_spy.exits is 0

        empty_statechart.dispatch(Event('local'))

        assert empty_statechart.is_active('spy')
        assert not empty_statechart.is_active('default')
        assert empty_statechart.is_active('local')
        assert state_spy.entries is 1
        assert state_spy.exits is 0
예제 #18
0
    def __init__(self, start, end, event=None, guard=None, action=None):
        self._logger = logging.getLogger(self.__class__.__name__)
        self.start = start
        self.end = end
        self.event = event
        self.guard = guard
        self.action = action

        if isinstance(event, str):
            self.event = Event(event)
        """ Used to store the states that will get activated """
        self.activate = list()
        """ Used to store the states that will get de-activated """
        self.deactivate = list()

        self._calculate_state_set(start=start, end=end)

        start.add_transition(self)
예제 #19
0
    def test_transition_action_function_with_event(self, empty_statechart):
        self.state = False

        def set_state(event):
            self.state = event.data['state']

        sc = empty_statechart
        initial = InitialState(sc)
        default = State(name='default', context=sc)
        next = State(name='next', context=sc)

        Transition(start=initial, end=default)
        Transition(start=default, end=next, event='next', action=set_state)

        sc.start()
        sc.dispatch(Event(name='next', data={'state': True}))

        assert self.state
예제 #20
0
    def test_simple_statechart_finished(self):
        statechart = Statechart(name='statechart')
        init = InitialState(statechart)
        default = State(name='default', context=statechart)
        final = FinalState(statechart)

        finish = Event('finish')

        Transition(start=init, end=default)
        Transition(start=default, end=final, event=finish)
        statechart.start()

        assert statechart.is_active('default')
        assert not statechart.is_finished()

        statechart.dispatch(finish)

        assert statechart.is_finished()
예제 #21
0
    def test_external_transition(self, empty_statechart):
        init = InitialState(empty_statechart)
        state_spy = self.StateSpy(name='spy', context=empty_statechart)

        Transition(start=init, end=state_spy)
        Transition(start=state_spy, end=state_spy, event='extern')

        empty_statechart.start()

        assert empty_statechart.is_active('spy')
        assert state_spy.entries is 1
        assert state_spy.exits is 0

        empty_statechart.dispatch(Event('extern'))

        # After dispatching the external event from the state spy, the
        # state should be deactivated and activated again.
        assert empty_statechart.is_active('spy')
        assert state_spy.entries is 2
        assert state_spy.exits is 1
예제 #22
0
    def test_transition_action_function_with_metadata(self, empty_statechart):
        sc = empty_statechart
        sc.metadata.state = True

        self.state = False

        def set_state(event):
            self.state = sc.metadata.state

        initial = InitialState(sc)
        default = State(name='default', context=sc)
        next = State(name='next', context=sc)

        Transition(start=initial, end=default)
        Transition(start=default, end=next, event='next', action=set_state)

        sc.start()
        sc.dispatch(Event('next'))

        assert self.state
예제 #23
0
    def test_transition_action_function(self, empty_statechart):
        self.state = False

        def set_state(state):
            self.state = bool(state)

        set_true = partial(set_state, True)

        sc = empty_statechart
        initial = InitialState(sc)
        default = State(name='default', context=sc)
        next = State(name='next', context=sc)

        Transition(start=initial, end=default)
        Transition(start=default, end=next, event='next', action=set_true)

        sc.start()
        sc.dispatch(Event('next'))

        assert self.state
예제 #24
0
    def test_top_level_internal_transition(self, empty_statechart):
        sc = empty_statechart
        sc_init = InitialState(sc)

        cs = CompositeState(name='cs', context=sc)
        cs_init = InitialState(cs)
        cs_default = State(name='cs_default', context=cs)

        Transition(start=sc_init, end=cs)
        Transition(start=cs_init, end=cs_default)

        test_event = Event('internal-event-trigger')
        InternalTransition(state=cs, event=test_event)

        sc.start()

        assert sc.is_active('cs_default')

        sc.dispatch(test_event)

        assert sc.is_active('cs_default')
예제 #25
0
    def test_default_transition_isnt_executed_from_unfinished_composite_state(
            self):
        statechart = Statechart(name='statechart')
        statechart_init = InitialState(statechart)

        composite_state = CompositeState(name='composite', context=statechart)
        comp_init = InitialState(composite_state)
        a = State(name='a', context=composite_state)

        Transition(start=statechart_init, end=composite_state)
        Transition(start=comp_init, end=a)

        b = State(name='b', context=statechart)

        Transition(start=composite_state, end=b)

        statechart.start()

        assert statechart.is_active('a')

        statechart.dispatch(Event('e'))

        assert statechart.is_active('a')
예제 #26
0
 def test_diff_data(self, event):
     diff_event = Event(name='diff_event', data={'a': 2})
     assert event != diff_event
예제 #27
0
 def test_diff_names(self, event):
     diff_event = Event(name='diff_event')
     assert diff_event is not event
예제 #28
0
 def test_create_event(self):
     Event(name='event')
예제 #29
0
def event():
    return Event(name='event', data={'a': 1})
예제 #30
0
    def test_activate_shallow_history_state(self):
        """
        statechart:

        statechart_init
                  |
        *** csa **********************          *** csb *************
        *                            *          *                   *
        *  csa_init-csa_hist         *          *  csb_init         *
        *              |             *  --J-->  *     |             *
        *              A  --I-->  B  *  <--K--  *     C  --L-->  D  *
        *                            *          *                   *
        ******************************          *********************
        """

        # Top level states
        statechart = Statechart(name='statechart')
        csa = CompositeState(name='csa', context=statechart)
        csb = CompositeState(name='csb', context=statechart)

        # Child states
        # statechart
        statechart_init = InitialState(statechart)
        # csa
        csa_init = InitialState(csa)
        csa_hist = ShallowHistoryState(context=csa)
        A = State(name='A', context=csa)
        B = State(name='B', context=csa)
        # csb
        csb_init = InitialState(csb)
        C = State(name='C', context=csb)
        D = State(name='D', context=csb)

        # Events
        I = Event(name='I')
        J = Event(name='J')
        K = Event(name='K')
        L = Event(name='L')

        # Transitions between states & event triggers
        Transition(start=statechart_init, end=csa)
        Transition(start=csa_init, end=csa_hist)
        Transition(start=csa_hist, end=A)
        Transition(start=A, end=B, event=I)
        Transition(start=csa, end=csb, event=J)
        Transition(start=csb, end=csa, event=K)
        Transition(start=csb_init, end=C)
        Transition(start=C, end=D, event=L)

        # Execute statechart
        statechart.start()
        statechart.dispatch(I)

        # Assert we have reached state B, history should restore this state
        assert statechart.is_active('B')

        statechart.dispatch(J)

        # Assert we have reached state C
        assert statechart.is_active('C')

        statechart.dispatch(K)

        # Assert the history state has restored state B
        assert statechart.is_active('B')