# 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 """ Constraints Defines a set of constraints with their related errors. These constraints are made to be used inside TutoriusProperties in order to limit the values that they might take. They can also be used to enforce a particular format or type for some properties. """ # For the File Constraint import os class Constraint(): """ Basic block for defining constraints on a TutoriusProperty. Every class inheriting from Constraint will have a validate function that will be executed when the property's value is to be changed. """ def validate(self, value): """ This function receives the value that is proposed as a new value for the property. It needs to raise an Error in the case where the value does not respect this constraint. """ raise NotImplementedError("Unable to validate a base Constraint") class ValueConstraint(Constraint): """ A value constraint contains a _limit member that can be used in a children class as a basic value. See UpperLimitConstraint for an exemple. """ def __init__(self, limit): self.limit = limit class UpperLimitConstraintError(Exception): pass class UpperLimitConstraint(ValueConstraint): def validate(self, value): """ Evaluates whether the given value is smaller than the limit. @raise UpperLimitConstraintError When the value is strictly higher than the limit. """ if self.limit is not None: if self.limit >= value: return raise UpperLimitConstraintError() return class LowerLimitConstraintError(Exception): pass class LowerLimitConstraint(ValueConstraint): def validate(self, value): """ If the value is lower than the limit, this function raises an error. @raise LowerLimitConstraintError When the value is strictly smaller than the limit. """ if self.limit is not None: if value >= self.limit: return raise LowerLimitConstraintError() return class MaxSizeConstraintError(Exception): pass class MaxSizeConstraint(ValueConstraint): def validate(self, value): """ Evaluate whether a given object is smaller than the given size when run through len(). Great for string, lists and the like. ;) @raise SizeConstraintError If the length of the value is strictly bigger than the limit. """ if self.limit is not None: if self.limit >= len(value): return raise MaxSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit)) return class MinSizeConstraintError(Exception): pass class MinSizeConstraint(ValueConstraint): def validate(self, value): """ Evaluate whether a given object is smaller than the given size when run through len(). Great for string, lists and the like. ;) @raise SizeConstraintError If the length of the value is strictly bigger than the limit. """ if self.limit is not None: if self.limit <= len(value): return raise MinSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit)) return class ColorConstraintError(Exception): pass class ColorArraySizeError(ColorConstraintError): pass class ColorTypeError(ColorConstraintError): pass class ColorValueError(ColorConstraintError): pass class ColorConstraint(Constraint): """ Validates that the value is an array of size 3 with three numbers between 0 and 255 (inclusively) in it. """ def validate(self, value): if len(value) != 3: raise ColorArraySizeError("The value is not an array of size 3") if not (type(value[0]) == type(22) and type(value[1]) == type(22) and type(value[2]) == type(22)): raise ColorTypeError("Not all the elements of the array are integers") if value[0] > 255 or value[0] <0: raise ColorValueError("Red value is not between 0 and 255") if value[1] > 255 or value[1] <0: raise ColorValueError("Green value is not between 0 and 255") if value[2] > 255 or value[2] <0: raise ColorValueError("Blue value is not between 0 and 255") return class BooleanConstraintError(Exception): pass class BooleanConstraint(Constraint): """ Validates that the value is either True or False. """ def validate(self, value): if value == True or value == False: return raise BooleanConstraintError("Value is not True or False") class EnumConstraintError(Exception): pass class EnumConstraint(Constraint): """ Validates that the value is part of a set of well-defined values. """ def __init__(self, accepted_values): """ Creates the constraint and stores the list of accepted values. @param correct_values A list that contains all the values that will be declared as satisfying the constraint """ self._accepted_values = accepted_values def validate(self, value): """ Ensures that the value that is passed is part of the list of accepted values. """ if not value in self._accepted_values: raise EnumConstraintError("Value is not part of the enumeration") return class FileConstraintError(Exception): pass class FileConstraint(Constraint): """ Ensures that the string given corresponds to an existing file on disk. """ def validate(self, value): # TODO : Decide on the architecture for file retrieval on disk # Relative paths? From where? Support macros? # FIXME This is a hack to make cases where a default file is not valid # work. It allows None values to be validated, though if value is None: return if not os.path.isfile(value): raise FileConstraintError("Non-existing file : %s"%value) return