From 0c3f127c86af818d260966d2292b199757087157 Mon Sep 17 00:00:00 2001 From: Simon Poirier Date: Sat, 11 Jul 2009 21:39:46 +0000 Subject: repackage --- (limited to 'tutorius/properties.py') diff --git a/tutorius/properties.py b/tutorius/properties.py new file mode 100644 index 0000000..34b508a --- /dev/null +++ b/tutorius/properties.py @@ -0,0 +1,323 @@ +# 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 sugar.tutorius.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( + 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() + +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 + + + 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 + +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 = self.validate(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. + """ + # TODO : Pending UAM check-in (LP 355199) + pass + +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 TAddonListProperty(TutoriusProperty): + """ + Reprensents an embedded tutorius Addon List Component. + See TAddonProperty + """ + def __init__(self): + super(TAddonProperty, self).__init__() + self.type = "addonlist" + self.default = [] + + -- cgit v0.9.1