Bryan Helmig

Co-founder of Zapier, speaker, musician and builder of things.

Working on a fun side project at HackComo, I needed a finite state machine. Well, this is a simple decorator for limiting when a method can be ran:

def fsm(target, whence='*', attr='state'):
    """
    `target` -> (str): REQUIRED
        should be the target state.
 
    `whence` -> (list): '*'
        should be a *list* of states that you can change from.
 
    `attr` -> (str): 'state'
        is the that of the attribute that represents state.
 
 
    Example:
 
        class Car(object):
            state = 'off'
 
            @fsm('on', whence=['off'])
            def turn_on(self):
                pass
 
            @fsm('off', whence=['on'])
            def turn_off(self):
                pass
 
        car = Car()
        # state is 'off'
 
        car.turn_off()              # raise Exception
        car.turn_off(silent=True)   # fails silently
        # state is still 'off'
 
        car.turn_on()
        # state is 'on
 
    """
 
    def dec(method):
        def inner(self, *args, **kwargs):
            silent = kwargs.pop('silent', False)
 
            # check that whence == curent state
            if whence == '*':
                pass
            elif getattr(self, attr) in whence:
                pass
            else:
                if not silent:
                    raise Exception('You shall not pass!')  #LOTR
                else:
                    return None
 
            result = method(self, *args, **kwargs)
            setattr(self, attr, target) # set state to target
 
            return result
        return inner
 
    return dec

Posted March 29, 2012 @ 11:18 am under Tech, Uncategorized.