Exemplo n.º 1
0
    def add_action(self, state, event, action):
        """
        Add a new action routine to the list of routines executed when the event is triggered in the specified state

        Arguments:
        state -- state portion of (state, event) tuple
        event -- event portion of (state, event) tuple
        action -- action routine to add to the actions list associated with (state, event) tuple

        Exceptions:
        DataStateError -- the specified state is not in the list of valid states
        StateMachineNonexistentEventError -- the specified event is not in the list of valid events
        StateMachineError - the specfied action routine is not a function or method
        """

        if state not in self._states:
            raise DataStateError(state)
        if event not in self._events:
            raise StateMachineNonexistentEventError(event)
        if not callable(action):
            raise StateMachineError("action is not a function: %s" %
                                    (action, ))
        if not self.__seas.has_key((state, event)):
            self.__seas[(state, event)] = []
        self.__seas[(state, event)] += [action]
Exemplo n.º 2
0
    def add_terminal_action(self, action, args={}):
        """
        Add a new action routine to the list of routines executed when the terminal state is reached

        Arguments:
        action -- action routine to add to the terminal actions list
        args -- dictionary of arguments to pass to the action routine

        Exceptions:
        StateMachineError - the specfied action routine is not a function or method
        """

        if not callable(action):
            raise StateMachineError("action is not a function: %s" %
                                    (action, ))
        if not isinstance(args, dict):
            raise StateMachineError("args is not a dictionary: %s" %
                                    (action, ))
        self.__terminal_actions += [(action, args)]
Exemplo n.º 3
0
    def __init__(self, spec, seas=None, terminal_actions=None):
        """
        Initialize a state machine instance

        Arguments:
        spec -- a dictionary passed to the underlying Data class (not used by the state machine)
        seas -- a dictionary mapping (state, event) tuples to lists of action routines
        terminal_actions -- list of action routines to be executed when the state machine reaches the terminal state; more
            specifically, each element in the list is a tuple of (action, args) where args is passed to action when it is called

        Exceptions:
        StateMachineError -- the state machine is incorrectly configured; accompanying message contains additional information
        """

        # if 'Progress' not in self._events:
        #     raise StateMachineError("progress event removed from list of events")
        if 'Terminal' not in self._states:
            raise StateMachineError(
                "terminal state removed from list of states")

        try:
            DataState.__init__(self, spec)
        except DataStateError, e:
            raise StateMachineError(e.args)
Exemplo n.º 4
0
    def update_seas(self, seas):
        '''change the statemachine states mid-flight.  It is the updater's 
        respomsibility to not break existing jobs by removing states that are
        currently in use.

        '''
        if seas != None:
            if not isinstance(seas, dict):
                raise StateMachineError(
                    "supplied SEAs parameter is not a dictionary")
            for key, actions in seas.iteritems():
                # verify that each key in the SEAs dict has a valid state and event
                if not isinstance(key, tuple) or len(key) != 2:
                    raise StateMachineError(
                        "SEAs dictionary contains an invalid (state, event) tuple: %s"
                        % (key, ))
                state, event = key
                if state not in self._states:
                    raise StateMachineError(
                        "SEAs dictionary contains an invalid state: %s" %
                        (state, ))
                if state == 'Terminal':
                    raise StateMachineError(
                        "SEAs dictionary must not contain entries for the 'Terminal' state"
                    )
                if event not in self._events:
                    raise StateMachineError(
                        "SEAs dictionary contains an invalid event: %s" %
                        (event, ))
                # verify that each value in the SEAs dict is a list of action functions
                if not isinstance(actions, list):
                    raise StateMachineError(
                        "entry %s in the SEAs dictionary is not a list: %s" %
                        (key, actions))
                for action in actions:
                    if not callable(action):
                        raise StateMachineError( \
                            "entry %s in the SEAs dictionary has an action that is not a function: %s" % (key, action))
            self.__seas = seas
        else:
            self.__seas = {}
Exemplo n.º 5
0
class StateMachine(DataState):
    """
    Generalized state machine driver
    
    Class attributes:
    _states -- list of states (must include the 'Terminal' state)
    _transitions -- list of legal state transitions in the form of (old state, new state) tuples
    _events -- list of events (may include the special 'Progress' state)
    _initial_state -- starting state (must be in the list of legal states)

    Methods:
    add_action -- add a new action routine to the list of routines associated with a (state, event) tuple
    add_terminal_action -- add a new action routine to the list of routines executed when the terminal state is reached
    trigger_event -- run the action routines associated with the current state and the supplied event

    Properties:
    _state - see DataState class
    _event -- event currently being triggered (None when not executing trigger_event routine)
    """

    _events = ['Progress']
    _states = DataState._states + ['Terminal']

    def __init__(self, spec, seas=None, terminal_actions=None):
        """
        Initialize a state machine instance

        Arguments:
        spec -- a dictionary passed to the underlying Data class (not used by the state machine)
        seas -- a dictionary mapping (state, event) tuples to lists of action routines
        terminal_actions -- list of action routines to be executed when the state machine reaches the terminal state; more
            specifically, each element in the list is a tuple of (action, args) where args is passed to action when it is called

        Exceptions:
        StateMachineError -- the state machine is incorrectly configured; accompanying message contains additional information
        """

        # if 'Progress' not in self._events:
        #     raise StateMachineError("progress event removed from list of events")
        if 'Terminal' not in self._states:
            raise StateMachineError(
                "terminal state removed from list of states")

        try:
            DataState.__init__(self, spec)
        except DataStateError, e:
            raise StateMachineError(e.args)

        if seas != None:
            if not isinstance(seas, dict):
                raise StateMachineError(
                    "supplied SEAs parameter is not a dictionary")
            for key, actions in seas.iteritems():
                # verify that each key in the SEAs dict has a valid state and event
                if not isinstance(key, tuple) or len(key) != 2:
                    raise StateMachineError(
                        "SEAs dictionary contains an invalid (state, event) tuple: %s"
                        % (key, ))
                state, event = key
                if state not in self._states:
                    raise StateMachineError(
                        "SEAs dictionary contains an invalid state: %s" %
                        (state, ))
                if state == 'Terminal':
                    raise StateMachineError(
                        "SEAs dictionary must not contain entries for the 'Terminal' state"
                    )
                if event not in self._events:
                    raise StateMachineError(
                        "SEAs dictionary contains an invalid event: %s" %
                        (event, ))
                # verify that each value in the SEAs dict is a list of action functions
                if not isinstance(actions, list):
                    raise StateMachineError(
                        "entry %s in the SEAs dictionary is not a list: %s" %
                        (key, actions))
                for action in actions:
                    if not callable(action):
                        raise StateMachineError( \
                            "entry %s in the SEAs dictionary has an action that is not a function: %s" % (key, action))
            self.__seas = seas
        else:
            self.__seas = {}

        if terminal_actions != None:
            if not isinstance(terminal_actions, list):
                raise StateMachineError("terminal_actions is not a list")
            item_num = 0
            for item in terminal_actions:
                if not isinstance(item, tuple) or len(item) != 2:
                    raise StateMachineError(
                        "terminal_actions entry %d is not a tuple of size 2: %s"
                        % (item_num, item))
                action, args = item
                if not callable(action):
                    raise StateMachineError("the action in entry %d of the terminal_actions is not a function: %s" % \
                        (item_num, action))
                if not isinstance(args, dict):
                    raise StateMachineError("the arguments in entry %d of the terminal_actions is not a dictionary: %s" % \
                        (item_num, action))
            self.__terminal_actions = terminal_actions
        else:
            self.__terminal_actions = []

        self.__event = None

        DataState._state.__set__(self, self._initial_state)