Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing self.sm.leaf_state from a event-handler returns wrong state #7

Closed
RaphaelMaschinsen opened this issue Apr 4, 2019 · 1 comment
Assignees
Labels

Comments

@RaphaelMaschinsen
Copy link

Hi

I came across this problem and it is very likely that i'm doing something wrong, however i couldn't figure out what it might be.

After i create an object that has a statemachine with entry-handlers for the states, when i call self.sm.leaf_state from within an entry-handler, it will always return the same state, even if the statemachine changes its state.

It's weird because if i access self.sm.leaf_state from outside the entry-handler, for example inside a test() function, it will return the correct states.

This script demonstrates my problem:

from pysm import StateMachine, State, Event

class TestSM():
    def __init__(self):
        self.sm = self._get_state_machine()

    def _get_state_machine(self):
        state_machine = StateMachine('Test')
        state_1 = State('One')
        state_2 = State('Two')

        state_machine.add_state(state_1, initial=True)
        state_machine.add_state(state_2)
        state_machine.add_transition(state_1, state_2, events=['change_state'])
        state_machine.add_transition(state_2, state_1, events=['change_state'])
        state_1.handlers = {'enter': self.entry_func}
        state_2.handlers = {'enter': self.entry_func}

        state_machine.initialize()
        return state_machine

    def entry_func(self, state, event):
        print('Entered State: ' + str(state.name))
        print(self.sm.leaf_state)

    def test(self):
        self.sm.dispatch(Event('change_state'))
        print(self.sm.leaf_state)
        self.sm.dispatch(Event('change_state'))
        print(self.sm.leaf_state)
        self.sm.dispatch(Event('change_state'))
        print(self.sm.leaf_state)

TEST_SM = TestSM()
TEST_SM.test()

The output i get (python 3.5.2):

Entered State: Two
<State One (0x10a4253c8)>
<State Two (0x10a425400)>
Entered State: One
<State One (0x10a4253c8)>
<State One (0x10a4253c8)>
Entered State: Two
<State One (0x10a4253c8)>
<State Two (0x10a425400)>

I would expect for both of the print(self.sm.leaf_state) to be the same. Am i doing something wrong?

Btw thanks again for the great library, i'm using it quite a lot on an ESP32.

pgularski pushed a commit that referenced this issue Apr 6, 2019
pgularski pushed a commit that referenced this issue Apr 7, 2019
pgularski pushed a commit that referenced this issue Apr 7, 2019
@pgularski pgularski added the bug label Apr 7, 2019
@pgularski pgularski self-assigned this Apr 7, 2019
@pgularski
Copy link
Owner

Hi Raphael,

Thanks for submitting this issue along with a great explanation and the code explaining the problem - in fact, I tweaked the code a bit and commited it in as one of the tests.

Initially I wasn't convinced it was a bug as I thought that a leaf_state should be used only from outside the machine state action. But then, as a second thought, I figured out some use cases where it might be useful to actually check the leaf_state from within a transition action (as you did).

So I fixed the issue and now the leaf_state is updated on every enter and exit action so that is always up to date. So far it was dynamically calculated and was returning a valid state only after a transition has settled.

The new approach has its implications though, as now it's illegal to set manually the state machine states once a machine got initialized. I mean it breaks (and actually always did) the machine state integrity. Therefore, the following code is no longer valid and is removed from the tests:

    # Brute force rollback of the previous state
    s3.state = s3.state_stack.pop()
    sm.state = sm.state_stack.pop()
    assert sm.state == s1
    assert sm.leaf_state == s1
    assert s3.state == s31
    assert s3.leaf_state == s31
    assert list(sm.state_stack.deque) == [s1, s2]
    assert list(s3.state_stack.deque) == []

Thanks again that you keep using the library and send me the issues you find. The feedback I got from you helps me a lot!

I released the fixed version as 0.3.9 - it's deployed to pypi.

Piotr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants