4

I am using Fysom to create FSM. I want to use callbacks in an other way:

TABLE = {
'initial': 'OFF',
'events': [{'name': 'load', 'src': 'OFF', 'dst': 'LOADED'},],
'callbacks': {'onload': myfunction,}}

fsm = Fysom(TABLE)

Here if I launch fsm.onload() it will execute myfunction. Instead I want If I launch myfunction() it will lunch fsm.onload().

I took a look on the script and related part is here:

def _enter_state(self, e):
    '''
        Executes the callback for onenter_state_ or on_state_.
    '''
    for fnname in ['onenter' + e.dst, 'on' + e.dst]:
        if hasattr(self, fnname):
            return getattr(self, fnname)(e)

I don't see how to change this peace of code for my purpose.

Katsu
  • 1,868
  • 4
  • 19
  • 28

1 Answers1

4

You cannot implement callbacks "in the other direction" without touching myfunction. A callback is actually an inverted call (hollywood principle), so the inverse of a callback is a simple call.

What this means is that myfunction should call the state machine transition directly. As a consequence, the state machine object must be in the scope of myfunction.

At this point you have some possibilities for implementation :

  • Have myfunction be a closure :

    def outer():
        fsm = Fysom(TABLE)
        def myfunction():
            print("I call the state machine transition when called")
            fsm.onload()
        return fsm
    
  • Use a global state machine or scope it in a class :

    class Foo(object):
    
        def __init__(self):
            self.fsm = Fysom(TABLE)
    
        def my_method(self):
            self.fsm.onload()
    
  • Create a decorator that calls the state transition method before/after executing the function. Then decorate away with

    fsm = Fysom(TABLE)
    
    @transition(fsm, "onload")
    def myfunction():
        pass
    

    Note that this also requires fsm to be defined in this scope.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
user3633465
  • 106
  • 1
  • First thanks' for help. I am using the 2nd point but actually I don't want to call it in my_method (that's the reason of my question). What I want is to define all function/method which will launch `self.fsm.onload()` (automatically) when called. And I want to define it in `TABLE`. As you said I cannot implement it without changing `myfunction`. Maybe I can if I change something in constructor (__init__) of class Foo and link between Foo and Fysom ? 3rd point: To my mind the decorator is exactly the same thing as calling it into the function. – Katsu May 14 '14 at 07:41
  • I don't see a way to do that right now. The fysom could probably rewrite the code of `myfunction` so that it calls `onload`, but that would be really messy. Maybe you can put a list of method names in `TABLE`, and then use `getattr`/`setattr` in the class constructor to replace the methods? That would look a lot like the decorated version, as you mentioned. – user3633465 May 14 '14 at 08:06
  • Ok so I finally choose the decorated version. It's easier because I don't really see how to implement with getattr/setattr version. Thank you for help. – Katsu May 15 '14 at 07:00