Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius
diff options
context:
space:
mode:
authorerick <erick@sugar-dev-erick.(none)>2009-10-30 22:21:54 (GMT)
committer erick <erick@sugar-dev-erick.(none)>2009-10-30 22:21:54 (GMT)
commita023cce7ec486c85234bc25ad1740191c920d454 (patch)
treed6ec477c638771e8545bde5d7338c9d5073a15db /tutorius
parent37e2ab5dd552be9aec49ccf774c90da8b962ea9f (diff)
Tutorial: Changed Tutorial.update_* to update properties instead of replacing the object, Moved validation of state name to a decorator to avoid dupliation
Diffstat (limited to 'tutorius')
-rw-r--r--tutorius/constraints.py22
-rw-r--r--tutorius/properties.py10
-rw-r--r--tutorius/tutorial.py171
3 files changed, 132 insertions, 71 deletions
diff --git a/tutorius/constraints.py b/tutorius/constraints.py
index e91f23a..519bce8 100644
--- a/tutorius/constraints.py
+++ b/tutorius/constraints.py
@@ -25,6 +25,12 @@ for some properties.
# For the File Constraint
import os
+class ConstraintException(Exception):
+ """
+ Parent class for all constraint exceptions
+ """
+ pass
+
class Constraint():
"""
Basic block for defining constraints on a TutoriusProperty. Every class
@@ -47,7 +53,7 @@ class ValueConstraint(Constraint):
def __init__(self, limit):
self.limit = limit
-class UpperLimitConstraintError(Exception):
+class UpperLimitConstraintError(ConstraintException):
pass
class UpperLimitConstraint(ValueConstraint):
@@ -64,7 +70,7 @@ class UpperLimitConstraint(ValueConstraint):
raise UpperLimitConstraintError()
return
-class LowerLimitConstraintError(Exception):
+class LowerLimitConstraintError(ConstraintException):
pass
class LowerLimitConstraint(ValueConstraint):
@@ -81,7 +87,7 @@ class LowerLimitConstraint(ValueConstraint):
raise LowerLimitConstraintError()
return
-class MaxSizeConstraintError(Exception):
+class MaxSizeConstraintError(ConstraintException):
pass
class MaxSizeConstraint(ValueConstraint):
@@ -99,7 +105,7 @@ class MaxSizeConstraint(ValueConstraint):
raise MaxSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
return
-class MinSizeConstraintError(Exception):
+class MinSizeConstraintError(ConstraintException):
pass
class MinSizeConstraint(ValueConstraint):
@@ -117,7 +123,7 @@ class MinSizeConstraint(ValueConstraint):
raise MinSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
return
-class ColorConstraintError(Exception):
+class ColorConstraintError(ConstraintException):
pass
class ColorArraySizeError(ColorConstraintError):
@@ -153,7 +159,7 @@ class ColorConstraint(Constraint):
return
-class BooleanConstraintError(Exception):
+class BooleanConstraintError(ConstraintException):
pass
class BooleanConstraint(Constraint):
@@ -165,7 +171,7 @@ class BooleanConstraint(Constraint):
return
raise BooleanConstraintError("Value is not True or False")
-class EnumConstraintError(Exception):
+class EnumConstraintError(ConstraintException):
pass
class EnumConstraint(Constraint):
@@ -190,7 +196,7 @@ class EnumConstraint(Constraint):
raise EnumConstraintError("Value is not part of the enumeration")
return
-class FileConstraintError(Exception):
+class FileConstraintError(ConstraintException):
pass
class FileConstraint(Constraint):
diff --git a/tutorius/properties.py b/tutorius/properties.py
index a675ba9..427222b 100644
--- a/tutorius/properties.py
+++ b/tutorius/properties.py
@@ -19,7 +19,7 @@ TutoriusProperties have the same behaviour as python properties (assuming you
also use the TPropContainer), with the added benefit of having builtin dialog
prompts and constraint validation.
"""
-from copy import copy
+from copy import copy, deepcopy
from .constraints import Constraint, \
UpperLimitConstraint, LowerLimitConstraint, \
@@ -93,8 +93,16 @@ class TPropContainer(object):
"""
Return the list of property names.
"""
+ # Why isn't it simply:
+ # return self._props.keys() ?
return object.__getattribute__(self, "_props").keys()
+ def get_properties_dict_copy(self):
+ """
+ Return a deep copy of the dictionary of properties from that object.
+ """
+ return deepcopy(self._props)
+
# Providing the hash methods necessary to use TPropContainers
# in a dictionary, according to their properties
def __hash__(self):
diff --git a/tutorius/tutorial.py b/tutorius/tutorial.py
index 0736bbb..9d8bc27 100644
--- a/tutorius/tutorial.py
+++ b/tutorius/tutorial.py
@@ -20,6 +20,9 @@
#TODO: Check for putting checks for validity of names as decorators
#TODO: For notification of modifications on the Tutorial check for GObject and PyDispatcher for inspiration
+from .constraints import ConstraintException
+
+
class Tutorial(object):
""" This class replaces the previous Tutorial class and
allows manipulation of the abstract representation
@@ -85,6 +88,23 @@ class Tutorial(object):
# existing state names
self._state_name_nb = 0
+ # Validation decorators to assert preconditions
+ def validateStateName(meth):
+ """
+ Assert that the state name found in the first part of the string
+ actually exists
+ """
+ def new(self, name, *args, **kwargs):
+ state_name = name
+
+ if name.find(Tutorial._NAME_SEPARATOR) != -1:
+ state_name = name[:name.find(Tutorial._NAME_SEPARATOR)]
+
+ if not self._state_dict.has_key(state_name):
+ raise LookupError("Tutorial: state <" + str(state_name) +\
+ "> is not defined")
+ return meth(self, name, *args, **kwargs)
+ return new
def add_state(self, action_list=[], transition_list=[]):
"""
@@ -160,37 +180,40 @@ class Tutorial(object):
# The unicity of the transition is validated by the state
return self._state_dict[state_name].add_transition(transition)
- def update_action(self, action_name, new_action):
+ @validateStateName
+ def update_action(self, action_name, new_properties):
"""
- Replace the action with action_name by new_action
-
- The action is validated.
+ Update the action with action_name with a property dictionary
+ new_properties. If one property update is invalid, the old
+ values are restored and an exception is raised.
- @param action_name The name of the action to replace
- @param new_action The action that will replace the old one
- @return The replaced action
+ @param action_name The name of the action to update
+ @param new_properties The properties that will update the action
+ @return old properties from the action
@raise LookupError if action_name doesn't exist
+ @raise ConstraintException if a property constraint is violated
"""
state_name = action_name[:action_name.find(Tutorial._NAME_SEPARATOR)]
- if not self._state_dict.has_key(state_name):
- raise LookupError("Tutorial: action <" + action_name +\
- "> is not defined")
-
- self._validate_action(new_action)
+ #TODO: We should validate that only properties defined on the action
+ # are passed in
- return self._state_dict[state_name].update_action(action_name, new_action)
+ return self._state_dict[state_name].update_action(action_name, new_properties)
- def update_transition(self, transition_name, new_transition):
+ @validateStateName
+ def update_transition(self, transition_name, new_properties=None, new_state=None):
"""
- Replace the transition with transition_name by new_transition
-
- The transition is validated.
+ Update the transition with transition_name with new properties and/or
+ a new state to transition to. A None value means that the corresponding
+ value won't be updated. If one property update is invalid, the old
+ values are restored and an exception is raised.
@param transition_name The name of the transition to replace
- @param new_transition The transition that will replace the old one
- @return The replaced transition
+ @param new_properties The properties that will update the transition
+ @param new_state The new state to transition to
+ @return a tuple (old_properties, old_state) with previous values
@raise LookupError if transition_name doesn't exist
+ @raise ConstraintException if a property constraint is violated
"""
state_name = transition_name[:transition_name.find(Tutorial._NAME_SEPARATOR)]
@@ -198,10 +221,16 @@ class Tutorial(object):
raise LookupError("Tutorial: transition <" + transition_name +\
"> is not defined")
- self._validate_transition(new_transition)
+ if new_state and not self._state_dict.has_key(new_state):
+ raise LookupError("Tutorial: destination state <" + new_state +\
+ "> is not defined")
+
+ #TODO: We should validate that only properties defined on the action
+ # are passed in
- return self._state_dict[state_name].update_transition(transition_name, new_transition)
+ return self._state_dict[state_name].update_transition(transition_name, new_properties, new_state)
+ @validateStateName
def delete_action(self, action_name):
"""
Delete the action identified by action_name.
@@ -212,12 +241,9 @@ class Tutorial(object):
"""
state_name = action_name[:action_name.find(Tutorial._NAME_SEPARATOR)]
- if not self._state_dict.has_key(state_name):
- raise LookupError("Tutorial: action <" + action_name +\
- "> is not defined")
-
return self._state_dict[state_name].delete_action(action_name)
-
+
+ @validateStateName
def delete_transition(self, transition_name):
"""
Delete the transition identified by transition_name.
@@ -228,12 +254,9 @@ class Tutorial(object):
"""
state_name = transition_name[:transition_name.find(Tutorial._NAME_SEPARATOR)]
- if not self._state_dict.has_key(state_name):
- raise LookupError("Tutorial: transition <" + transition_name +\
- "> is not defined")
-
return self._state_dict[state_name].delete_transition(transition_name)
+ @validateStateName
def delete_state(self, state_name):
"""
Delete the state, delete all the actions and transitions
@@ -250,10 +273,6 @@ class Tutorial(object):
"""
if state_name == Tutorial.INIT or state_name == Tutorial.END:
raise StateDeletionError("<" + state_name + "> cannot be deleted")
-
- if not self._state_dict.has_key(state_name):
- raise LookupError("Tutorial: state <" + transition_name +\
- "> is not defined")
next_states = set(self.get_following_states_dict(state_name).values())
previous_states = set(self.get_previous_states_dict(state_name).values())
@@ -519,6 +538,7 @@ class Tutorial(object):
"""
return str(self._state_dict)
+
class State(object):
"""
This is a step in a tutorial. The state represents a collection of actions
@@ -581,25 +601,31 @@ class State(object):
return self._actions.pop(action_name)
else:
raise LookupError("Tutorial.State: action <" + action_name + "> is not defined")
-
- def update_action(self, action_name, new_action):
+
+ def update_action(self, action_name, new_properties):
"""
- Replace the action with action_name by new_action
-
- @param action_name The name of the action to replace
- @param new_action The action that will replace the old one
- @return The replaced action
+ Update the action with action_name with a property dictionary
+ new_properties. If one property update is invalid, the old
+ values are restored and an exception is raised.
+
+ @param action_name The name of the action to update
+ @param new_properties The properties that will update the action
+ @return The old properties from the action
@raise LookupError if action_name doesn't exist
+ @raise ConstraintException if a property constraint is violated
"""
- # TODO: For now let's just replace the action with a new one,
- # we should check to see if we need a replace or an update
- # semantic for this update method
- if self._actions.has_key(action_name):
- old_action = self._actions.pop(action_name)
- self._actions[action_name] = new_action
- return old_action
- else:
+ if not self._actions.has_key(action_name):
raise LookupError("Tutorial.State: action <" + action_name + "> is not defined")
+
+ action = self._actions[action_name]
+ old_properties = action.get_properties_dict_copy()
+ try:
+ for property_name, property_value in new_properties.iteritems():
+ action.__setattr__(property_name, property_value)
+ return old_properties
+ except ConstraintException, e:
+ action._props = old_properties
+ raise e
def get_action_dict(self):
"""
@@ -635,25 +661,46 @@ class State(object):
self._transitions[transition_name] = new_transition
return transition_name
- def update_transition(self, transition_name, new_transition):
+ def update_transition(self, transition_name, new_properties=None, new_state=None):
"""
- Replace the transition with transition_name by new_transition
-
+ Update the transition with transition_name with new properties and/or
+ a new state to transition to. A None value means that the corresponding
+ value won't be updated. If one property update is invalid, the old
+ values are restored and an exception is raised.
+
@param transition_name The name of the transition to replace
- @param new_transition The transition that will replace the old one
- @return The replaced transition
+ @param new_properties The properties that will update the event on the transition
+ @param new_state The new state to transition to
+ @return a tuple (old_properties, old_state) with previous values
@raise LookupError if transition_name doesn't exist
+ @raise ConstraintException if a property constraint is violated
"""
- # TODO: For now let's just replace the transition with a new one,
- # we should check to see if we need a replace or an update
- # semantic for this update method
- if self._transitions.has_key(transition_name):
- old_transition = self._transitions.pop(transition_name)
- self._transitions[transition_name] = new_transition
- return old_transition
- else:
+ if not self._transitions.has_key(transition_name):
raise LookupError("Tutorial.State: transition <" + transition_name + "> is not defined")
+ transition = self._transitions[transition_name]
+
+ tmp_event = transition[0]
+ tmp_state = transition[1]
+
+ old_properties = transition[0].get_properties_dict_copy()
+ old_state = transition[1]
+
+ if new_properties:
+ try:
+ for property_name, property_value in new_properties.iteritems():
+ tmp_event.__setattr__(property_name, property_value)
+ except ConstraintException, e:
+ tmp_event._props = old_properties
+ raise e
+
+ if new_state:
+ tmp_state = new_state
+
+ self._transitions[transition_name] = (tmp_event, tmp_state)
+
+ return (old_properties, old_state)
+
def delete_transition(self, transition_name):
"""
Delete the transition with the name transition_name