Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/tutorius/properties.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/tutorius/properties.py')
-rw-r--r--src/tutorius/properties.py363
1 files changed, 363 insertions, 0 deletions
diff --git a/src/tutorius/properties.py b/src/tutorius/properties.py
new file mode 100644
index 0000000..a675ba9
--- /dev/null
+++ b/src/tutorius/properties.py
@@ -0,0 +1,363 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+This module contains properties class that can be included in other types.
+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 .constraints import Constraint, \
+ UpperLimitConstraint, LowerLimitConstraint, \
+ MaxSizeConstraint, MinSizeConstraint, \
+ ColorConstraint, FileConstraint, BooleanConstraint, EnumConstraint
+
+class TPropContainer(object):
+ """
+ A class containing properties. This does the attribute wrapping between
+ the container instance and the property value. As properties are on the
+ containing classes, they allow static introspection of those types
+ at the cost of needing a mapping between container instances, and
+ property values. This is what TPropContainer does.
+ """
+ def __init__(self):
+ """
+ Prepares the instance for property value storage. This is done at
+ object initialization, thus allowing initial mapping of properties
+ declared on the class. Properties won't work correctly without
+ this call.
+ """
+ # create property value storage
+ object.__setattr__(self, "_props", {})
+ for attr_name in dir(type(self)):
+ propinstance = object.__getattribute__(self, attr_name)
+ if isinstance(propinstance, TutoriusProperty):
+ # only care about TutoriusProperty instances
+ propinstance.tname = attr_name
+ self._props[attr_name] = propinstance.validate(
+ copy(propinstance.default))
+
+ def __getattribute__(self, name):
+ """
+ Process the 'fake' read of properties in the appropriate instance
+ container. Pass 'real' attributes as usual.
+ """
+ try:
+ props = object.__getattribute__(self, "_props")
+ except AttributeError:
+ # necessary for deepcopy as order of init can't be guaranteed
+ object.__setattr__(self, "_props", {})
+ props = object.__getattribute__(self, "_props")
+
+ try:
+ # try gettin value from property storage
+ # if it's not in the map, it's not a property or its default wasn't
+ # set at initialization.
+ return props[name]
+ except KeyError:
+ return object.__getattribute__(self, name)
+
+ def __setattr__(self, name, value):
+ """
+ Process the 'fake' write of properties in the appropriate instance
+ container. Pass 'real' attributes as usual.
+
+ @param name the name of the property
+ @param value the value to assign to name
+ @return the setted value
+ """
+ props = object.__getattribute__(self, "_props")
+ try:
+ # We attempt to get the property object with __getattribute__
+ # to work through inheritance and benefit of the MRO.
+ return props.__setitem__(name,
+ object.__getattribute__(self, name).validate(value))
+ except AttributeError:
+ return object.__setattr__(self, name, value)
+
+ def get_properties(self):
+ """
+ Return the list of property names.
+ """
+ return object.__getattribute__(self, "_props").keys()
+
+ # Providing the hash methods necessary to use TPropContainers
+ # in a dictionary, according to their properties
+ def __hash__(self):
+ #Return a hash of properties (key, value) sorted by key
+ #We need to transform the list of property key, value lists into
+ # a tuple of key, value tuples
+ return hash(tuple(map(tuple,sorted(self._props.items(), cmp=lambda x, y: cmp(x[0], y[0])))))
+
+ def __eq__(self, e2):
+ return isinstance(e2, type(self)) and self._props == e2._props
+
+ # Adding methods for pickling and unpickling an object with
+ # properties
+ def __getstate__(self):
+ return self._props.copy()
+
+ def __setstate__(self, dict):
+ self._props.update(dict)
+
+class TutoriusProperty(object):
+ """
+ The base class for all actions' properties. The interface is the following :
+
+ value : the value of the property
+
+ type : the type of the property
+
+ get_contraints() : the constraints inserted on this property. They define
+ what is acceptable or not as values.
+ """
+ def __init__(self):
+ super(TutoriusProperty, self).__init__()
+ self.type = None
+ self._constraints = None
+ self.default = None
+
+ def get_constraints(self):
+ """
+ Returns the list of constraints associated to this property.
+ """
+ if self._constraints is None:
+ self._constraints = []
+ for i in dir(self):
+ typ = getattr(self, i)
+ if isinstance(typ, Constraint):
+ self._constraints.append(i)
+ return self._constraints
+
+ def validate(self, value):
+ """
+ Validates the value of the property. If the value does not respect
+ the constraints on the property, this method will raise an exception.
+
+ The exception should be of the type related to the constraint that
+ failed. E.g. When a int is to be set with a value that
+ """
+ for constraint_name in self.get_constraints():
+ constraint = getattr(self, constraint_name)
+ constraint.validate(value)
+ return value
+
+class TAddonListProperty(TutoriusProperty):
+ """
+ Stores an addon component list as a property.
+ The purpose of this class is to allow correct mapping of properties
+ through encapsulated hierarchies.
+ """
+ pass
+
+class TIntProperty(TutoriusProperty):
+ """
+ Represents an integer. Can have an upper value limit and/or a lower value
+ limit.
+ """
+
+ def __init__(self, value, lower_limit=None, upper_limit=None):
+ TutoriusProperty.__init__(self)
+ self.type = "int"
+ self.upper_limit = UpperLimitConstraint(upper_limit)
+ self.lower_limit = LowerLimitConstraint(lower_limit)
+
+ self.default = self.validate(value)
+
+class TFloatProperty(TutoriusProperty):
+ """
+ Represents a floating point number. Can have an upper value limit and/or
+ a lower value limit.
+ """
+ def __init__(self, value, lower_limit=None, upper_limit=None):
+ TutoriusProperty.__init__(self)
+ self.type = "float"
+
+ self.upper_limit = UpperLimitConstraint(upper_limit)
+ self.lower_limit = LowerLimitConstraint(lower_limit)
+
+ self.default = self.validate(value)
+
+class TStringProperty(TutoriusProperty):
+ """
+ Represents a string. Can have a maximum size limit.
+ """
+ def __init__(self, value, size_limit=None):
+ TutoriusProperty.__init__(self)
+ self.type = "string"
+ self.size_limit = MaxSizeConstraint(size_limit)
+
+ self.default = self.validate(value)
+
+class TArrayProperty(TutoriusProperty):
+ """
+ Represents an array of properties. Can have a maximum number of element
+ limit, but there are no constraints on the content of the array.
+ """
+ def __init__(self, value, min_size_limit=None, max_size_limit=None):
+ TutoriusProperty.__init__(self)
+ self.type = "array"
+ self.max_size_limit = MaxSizeConstraint(max_size_limit)
+ self.min_size_limit = MinSizeConstraint(min_size_limit)
+ self.default = tuple(self.validate(value))
+
+ #Make this thing hashable
+ def __setstate__(self, state):
+ self.max_size_limit = MaxSizeConstraint(state["max_size_limit"])
+ self.min_size_limit = MinSizeConstraint(state["min_size_limit"])
+ self.value = state["value"]
+
+ def __getstate__(self):
+ return dict(
+ max_size_limit=self.max_size_limit.limit,
+ min_size_limit=self.min_size_limit.limit,
+ value=self.value,
+ )
+class TColorProperty(TutoriusProperty):
+ """
+ Represents a RGB color with 3 8-bit integer values.
+
+ The value of the property is the array [R, G, B]
+ """
+ def __init__(self, red=None, green=None, blue=None):
+ TutoriusProperty.__init__(self)
+ self.type = "color"
+
+ self.color_constraint = ColorConstraint()
+
+ self._red = red or 0
+ self._green = green or 0
+ self._blue = blue or 0
+
+ self.default = self.validate([self._red, self._green, self._blue])
+
+class TFileProperty(TutoriusProperty):
+ """
+ Represents a path to a file on the disk.
+ """
+ def __init__(self, path):
+ """
+ Defines the path to an existing file on disk file.
+
+ For now, the path may be relative or absolute, as long as it exists on
+ the local machine.
+ TODO : Make sure that we have a file scheme that supports distribution
+ on other computers (LP 355197)
+ """
+ TutoriusProperty.__init__(self)
+
+ self.type = "file"
+
+ self.file_constraint = FileConstraint()
+
+ self.default = self.validate(path)
+
+class TEnumProperty(TutoriusProperty):
+ """
+ Represents a value in a given enumeration. This means that the value will
+ always be one in the enumeration and nothing else.
+
+ """
+ def __init__(self, value, accepted_values):
+ """
+ Creates the enumeration property.
+
+ @param value The initial value of the enum. Must be part of
+ accepted_values
+ @param accepted_values A list of values that the property can take
+ """
+ TutoriusProperty.__init__(self)
+
+ self.type = "enum"
+
+ self.enum_constraint = EnumConstraint(accepted_values)
+
+ self.default = self.validate(value)
+
+class TBooleanProperty(TutoriusProperty):
+ """
+ Represents a True or False value.
+ """
+ def __init__(self, value=False):
+ TutoriusProperty.__init__(self)
+
+ self.type = "boolean"
+
+ self.boolean_constraint = BooleanConstraint()
+
+ self.default = self.validate(value)
+
+class TUAMProperty(TutoriusProperty):
+ """
+ Represents a widget of the interface by storing its UAM.
+ """
+ def __init__(self, value=None):
+ TutoriusProperty.__init__(self)
+
+ self.type = "uam"
+
+ self.default = self.validate(value)
+
+class TAddonProperty(TutoriusProperty):
+ """
+ Reprensents an embedded tutorius Addon Component (action, trigger, etc.)
+ The purpose of this class is to flag the container for proper
+ serialization, as the contained object can't be directly dumped to text
+ for its attributes to be saved.
+ """
+ class NullAction(TPropContainer):
+ def do(self): pass
+ def undo(self): pass
+
+ def __init__(self):
+ super(TAddonProperty, self).__init__()
+ self.type = "addon"
+ self.default = self.NullAction()
+
+ def validate(self, value):
+ if isinstance(value, TPropContainer):
+ return super(TAddonProperty, self).validate(value)
+ raise ValueError("Expected TPropContainer instance as TaddonProperty value")
+
+class TEventType(TutoriusProperty):
+ """
+ Represents an GUI signal for a widget.
+ """
+ def __init__(self, value):
+ super(TEventType, self).__init__()
+ self.type = "gtk-signal"
+
+ self.default = self.validate(value)
+
+class TAddonListProperty(TutoriusProperty):
+ """
+ Reprensents an embedded tutorius Addon List Component.
+ See TAddonProperty
+ """
+ def __init__(self):
+ TutoriusProperty.__init__(self)
+ self.type = "addonlist"
+ self.default = []
+
+ def validate(self, value):
+ if isinstance(value, list):
+ for component in value:
+ if not (isinstance(component, TPropContainer)):
+ raise ValueError("Expected a list of TPropContainer instances inside TAddonListProperty value, got a %s" % (str(type(component))))
+ return value
+ raise ValueError("Value proposed to TAddonListProperty is not a list")
+