GithubHelp home page GithubHelp logo

Comments (4)

ostetzer avatar ostetzer commented on August 25, 2024 1

Thank you so much for this detailed answer with even different options. Yes, please add this to the documentation asI hope it might help others too. I like the decorator version and will implement this. Thanks a lot! :-)

from python-statemachine.

ostetzer avatar ostetzer commented on August 25, 2024

I have experimented a bit more. When I define the event like this:

stop = running.to(standby, event="shutdown_cycle")
shutdown = standby.to(off, event="shutdown_cycle")

the function on_stop() or on_shutdown() is also not called, when I call the event like in the example above.

from python-statemachine.

fgmacedo avatar fgmacedo commented on August 25, 2024

Hi @ostetzer , how are you? Thanks for getting in touch.

Sorry about the misleading behavior. This theme is a huge opportunity to improve the docs.

This is indeed the expected behavior... To make it clear, I've created a graph representation of the example state machine you provided:

>>> test_machine._graph().write_png("test_FSM.png")

test_FSM

So, what's going on?

Note that on this graph, you have the transitions represented as the edges (arcs) that connect the states, and the events as labels attached to these edges.

Note that the transition is created using the pattern <origin>.to(<destination>), as in running.to(standby) by example.

When we assign the transition list to a variable at the class level, we're defining an event. So by declaring stop = running.to(standby), we're defining an event called stop, that will be bound to the transition running.to(standby).

And by declaring shutdown_cycle = stop | shutdown, we're assigning the two list of transitions at stop and shutdown to another event called shutdown_cycle.

Given that you're binding actions by naming convention, by definition:

The action will be registered for every Transition associated with the event.

The key here is that using this naming convention pattern, the action is associated with the event name, not with the transition. The action name matters and must match the pattern of the event.

Why the actions on_stop or on_shutdown are not performed when I trigger the event shutdown_cycle:
Answer: Because these actions are related to the events stop or shutdown, not to the event shutdown_cycle.

How to accomplish the expected behaviour

You can explicitly bind the action to the transition itself, using params, like running.to(standby, on="on_stop"). Note that now the name of the event does not matter, the only requirement is to have a method, attribute or property with the name specified.

class test_FSM(StateMachine):
    init = State(initial=True)
    standby = State()
    running = State()
    off = State(final=True)

    start = init.to(standby)
    run = standby.to(running)
    stop = running.to(standby, on="on_stop")
    shutdown = standby.to(off, on="on_shutdown")

    shutdown_cycle = stop | shutdown

    def __init__(self):
        super().__init__()

    def on_start(self):
        print("initialize to standby")

    def on_run(self):
        print("changed to running")

    def on_stop(self):
        print("stopping")

    def on_shutdown(self):
        print("shutting down")

So if you go with this alternative, I suggest changing the action name to another thing not similar to the naming convention, just to make explicitly that you're binding an action to the transition itself.

Like this:

class test_FSM(StateMachine):
    init = State(initial=True)
    standby = State()
    running = State()
    off = State(final=True)

    start = init.to(standby)
    run = standby.to(running)
    stop = running.to(standby, on="_on_stop")
    shutdown = standby.to(off, on="_on_shutdown")

    shutdown_cycle = stop | shutdown

    def __init__(self):
        super().__init__()

    def on_start(self):
        print("initialize to standby")

    def on_run(self):
        print("changed to running")

    def _on_stop(self):
        print("stopping")

    def _on_shutdown(self):
        print("shutting down")
        

The last possibility is to explicitly bind the action to a transitions list using decorators, again, with this binding the name of the action method does not matter.

class test_FSM(StateMachine):
    init = State(initial=True)
    standby = State()
    running = State()
    off = State(final=True)

    start = init.to(standby)
    run = standby.to(running)
    stop = running.to(standby)
    shutdown = standby.to(off)

    shutdown_cycle = stop | shutdown

    def __init__(self):
        super().__init__()

    def on_start(self):
        print("initialize to standby")

    def on_run(self):
        print("changed to running")

    @stop.on
    def _on_stop(self):
        print("stopping")

    @shutdown.on
    def _on_shutdown(self):
        print("shutting down")

Extra

Just to make a point on how things work internally, the <event_name> = <source_state>.to(<target_state>) is only syntatic sugar for <source_state>.to(<target_state>, event="<event_name>).

Example that also works as you expect:

class test_FSM(StateMachine):
    init = State(initial=True)
    standby = State()
    running = State()
    off = State(final=True)

    init.to(standby, event="start")
    standby.to(running, event="run")
    running.to(standby, event=["stop", "shutdown_cycle"], on="_on_stop")
    standby.to(off, event=["shutdown", "shutdown_cycle"], on="_on_shutdown")

    def __init__(self):
        super().__init__()

    def on_start(self):
        print("initialize to standby")

    def on_run(self):
        print("changed to running")

    def _on_stop(self):
        print("stopping")

    def _on_shutdown(self):
        print("shutting down")

What do you think about this alternative syntax? :)

Please let me know if I have clarified the behavior, or if you have any other questions.

from python-statemachine.

fgmacedo avatar fgmacedo commented on August 25, 2024

You're very welcome! I'm glad the options were helpful. I'll definitely document this for others too. Best!

from python-statemachine.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.