class Tutorial(object): """ This class replaces the previous Tutorial class and allows manipulation of the abstract representation of a tutorial as a state machine """ _INIT = "INIT" _END = "END" def __init__(self, name, state_dict=None): """ The constructor for the Tutorial. By default, the tutorial contains only an initial state and an end state. The initial state doesn't contain any action or transition. The end state doesn't contain any action either. If state_dict is provided, a valid initial state and an end state must be provided. @param name The name of the tutorial @param state_dict optional, a valid dictionary of states """ self.name = name # We will use an adjacency list representation through the # usage of state objects because our graph representation # is really sparse an mostly linear, for a brief # example of graph programming in python see: # http://www.python.org/doc/essays/graphs self._state_dict = state_dict or \ {Tutorial._INIT:State(name=Tutorial._INIT),\ Tutorial._END:State(name=Tutorial._END)} # Minimally check for the presence of an INIT and an END # state if not self._state_dict.has_key(Tutorial._INIT): raise Exception("No INIT state found in state_dict") if not self._state_dict.has_key(Tutorial._END): raise Exception("No END state found in state_dict") self.validate() # Initialize variables for generating unique names # TODO: We should take the max number from the # existing state names self._state_name_nb = 0 def add_state(self, action_list=[], transition_list=[]): """ Add a new state to the state machine. The state is initialized with the action list and transition list and a new unique name is returned for this state. The action is added using add_action. The transitions is added using add_transition. @param action_list The list of valid actions for this state @param transition_list The list of valid transitions @return string unique name for this state """ name = self._generate_unique_state_name() for action in action_list: self._validate_action(action) for transition in transition_list: self._validate_transition(transition) state = State(name, action_list, transition_list) if self._state_dict.has_key(name): raise Exception("Name: " + name + " already exists, could not\ add a new state.") self._state_dict[name] = state return name def add_action(self, state_name, action): """ Add an action to a specific state. A unique name for this tutorial is generated to refer precisely to this action and is returned. The action is validated. @param state_name The name of the state to add an action to @param action The action to be added @return unique name for this action """ return "State/Action" def add_transition(self, state_name, transition): """ Add a transition to a specific state. A unique name for this tutorial is generated to refer precisely to this transition and is returned. Inserting a duplicate transition will raise an exception. The transition is validated. @param state_name The name of the state to add a transition to @param transition The transition to be added @return unique name for this action @raise TransitionAlreadyExists """ return "State/Transition" def update_action(self, action_name, action): """ Update the properties of a specific action with a copy of the properties of the action passed in. The action is validated. @param action_name The name of the action to update @param action An action with the properties to copy from @return action_name if the update was successful, False otherwise """ return action_name def update_transition(self, transition_name, transition): """ Update the properties of a specific transition with a copy of the properties of the transition passed in. The transition is validated. @param transition_name The name of the transition to update @param transition An transition with the properties to copy from @return transition_name if the update was successful, False otherwise """ return transition_name def delete_action(self, action_name): """ Delete the action identified by action_name. @param action_name The name of the action to be deleted @return the action that has been deleted """ return None def delete_transition(self, transition_name): """ Delete the transition identified by transition_name. @param transition_name The name of the transition to be deleted @return the transition that has been deleted """ return None def delete_state(self, state_name): """ Delete the state, delete all the actions and transitions in this state, update the transitions from the state that pointed to this one to the next state and remove all the unreachable states recursively. @param state_name The name of the state to remove """ pass def get_actions(self, state_name): """ @param state_name The name of the state to list actions from @return A list of actions for state_name """ pass def get_events(self, state_name): """ @param state_name The name of the state to list actions from @return A list of events for state_name """ pass def get_following_states(self, state_name): """ Returns a tuple of the names of the states that point to the given state. If there is no such state, the function raises a KeyError. @param state_name The name of the state to analyse @raise KeyError When there is no state by this name in the FSM """ pass def get_previous_states(self, state_name): """ Returns a tuple of the names of the state that can transition to the given state. If there is no such state, the function raises a KeyError. @param state_name The name of the state that the returned states might transition to. @raise KeyError When there is no state by this name in the FSM """ pass def _validate_action(self, action): """ Validate that an action conforms to what we expect, throws an exception otherwise. @param action The action to validate @except InvalidAction if the action fails to conform to what we expect """ pass def _validate_action_name(self, action_name): """ Check if action_name exists. @param action_name The name to check @except UnknownName if the name is not present in the Tutorial """ pass def _validate_transition(self, transition): """ Validate that a transition conforms to what we expect, throws an exception otherwise. @param transition The transition to validate @except InvalidTransition if the transition fails to conform to what we expect """ pass def _validate_transition_name(self, transition_name): """ Check if transition_name exists. @param transition_name The name to check @except UnknownName if the name is not present in the Tutorial """ pass def validate(self): """ Validate the state machine for a serie of properties: 1. No unreachable states 2. No dead end state (except END) 3. No branching in the main path 4. No loop in the main path 5. ... Throw an exception for the first condition that is not met. """ pass def _generate_unique_state_name(self): name = "State" + str(self._state_name_nb) self._state_name_nb += 1 return name def __str__(self): """ Return a string representation of the tutorial """ return "" class State(object): """ This is a step in a tutorial. The state represents a collection of actions to undertake when entering the state, and a series of transitions to lead to next states. This class is not meant to be used explicitly as no validation is done on inputs, the validation should be done by the containing class. """ def __init__(self, name="", action_list=[], transition_list=[]): """ Initializes the content of the state, like loading the actions that are required and building the correct tests. @param action_list The list of actions to execute when entering this state @param transition_list A list of tuples of the form (event, next_state_name), that explains the outgoing links for this state """ object.__init__(self) self.name = name self._actions = {} for action in action_list: self._actions[self._generate_unique_action_name(action)] = action self._transitions = {} for transition in transition_list: self._transitions[self._generate_unique_transition_name(transition)] = transition # Action manipulations def add_action(self, new_action): """ Adds an action to the state (only if it wasn't added before) @param new_action The new action to execute when in this state @return a unique name for this action """ if new_action not in self._actions: self._actions.append(new_action) return True return False def delete_action(self, action_name): """ Delete the action with the name action_name returned when the action was added or when they are listed @param action_name The name of the action to delete @return True if the action existed of False if no action had this name """ pass def update_action(self, action_name, action): """ Update the action with the name action_name with the properties from action @param action_name The name of the action to update @param action The action whose properties are copied over @return True if action_name existed and the properties were valid, False otherwise """ pass def get_action_dict(self): """ @return A dictionary of actions that the state will execute """ return self._actions def delete_actions(self): """ Removes all the action associated with this state. A cleared state will not do anything when entered or exited. """ self._actions = {} # Transition manipulations def add_transition(self, transition): """ Adds a transition from this state to another state. The same transition may not be added twice. @param transition The new transition. @return A unique name for the transition could be added, False otherwise """ return False def update_transition(self, transition_name, transition): """ Update the transition with the name transition_name with the properties from transition @param transition_name The name of the transition to update @param transition The transition whose properties are copied over @return True if transition_name existed and the properties were valid, False otherwise """ pass def delete_transition(self, transition_name): """ Delete the transition with the name transition_name @param transition_name The name of the transition to delete @return True if transition_name existed, False otherwise """ pass def get_transition_dict(self): """ @return The dictionary of transitions associated with this state. """ return self._transitions def delete_transitions(self): """ Delete all the transitions associated with this state. """ self._transitions = {} def _generate_unique_action_name(self, action): """ Returns a unique name for the action in this state @param action The action to generate a name for @return A name garanteed to be unique within this state """ return None def _generate_unique_transition_name(self, transition): """ Returns a unique name for the transition in this state @param transition The transition to generate a name for @return A name garanteed to be unique within this state """ return None ################## Error Handling and Exceptions ############################## class TransitionAlreadyExists(Exception): """ Raised when a duplicate transition is added to a state """ pass