add custom validator
authorgwen <gremond@cadoles.com>
Mon, 19 Nov 2012 08:51:40 +0000 (09:51 +0100)
committergwen <gremond@cadoles.com>
Mon, 19 Nov 2012 08:51:40 +0000 (09:51 +0100)
tiramisu/config.py
tiramisu/option.py

index e97323e..0b8dbbe 100644 (file)
@@ -40,6 +40,8 @@ class Config(object):
     #mandatory means: a mandatory option has to have a value that is not None
     _cfgimpl_mandatory = True
     _cfgimpl_frozen = True
+    #enables validation function for options if set
+    _cfgimpl_validator = False
     _cfgimpl_owner = default_owner
     _cfgimpl_toplevel = None
 
@@ -248,9 +250,9 @@ class Config(object):
                             return value
                     else:
                         return value
+                rootconfig = self._cfgimpl_get_toplevel()
                 try:
-                    result = opt_or_descr.getcallback_value(
-                            self._cfgimpl_get_toplevel())
+                    result = opt_or_descr.getcallback_value(rootconfig)
                 except NoValueReturned, err:
                     pass
                 else:
@@ -264,7 +266,8 @@ class Config(object):
                             raise ConfigError('invalid calculated value returned'
                                 ' for option {0} : shall not be a list'.format(name))
                         _result = result
-                    if _result != None and not opt_or_descr.validate(_result):
+                    if _result != None and not opt_or_descr.validate(_result,
+                                rootconfig._cfgimpl_validator):
                         raise ConfigError('invalid calculated value returned'
                             ' for option {0}'.format(name))
                     self._cfgimpl_values[name] = _result
@@ -455,6 +458,7 @@ class Config(object):
         rootconfig.cfgimpl_disable_property('hidden')
         rootconfig.cfgimpl_enable_property('disabled')
         rootconfig._cfgimpl_mandatory = True
+        rootconfig._cfgimpl_validator = True
 
     def cfgimpl_read_write(self):
         "convenience method to freeze, hidde and disable"
index e69ea2e..5b865fc 100644 (file)
@@ -20,6 +20,7 @@
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
+from types import FunctionType
 from tiramisu.basetype import HiddenBaseType, DisabledBaseType
 from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
     RequiresError, RequirementRecursionError, MandatoryError,
@@ -92,7 +93,7 @@ class Option(HiddenBaseType, DisabledBaseType):
     _force_default_on_freeze = False
     def __init__(self, name, doc, default=None, default_multi=None,
                  requires=None, mandatory=False, multi=False, callback=None,
-                 callback_params=None):
+                 callback_params=None, validator=None, validator_args={}):
         """
         :param default: ['bla', 'bla', 'bla']
         :param default_multi: 'bla' (used in case of a reset to default only at
@@ -103,6 +104,14 @@ class Option(HiddenBaseType, DisabledBaseType):
         self._requires = requires
         self._mandatory = mandatory
         self.multi = multi
+        self._validator = None
+        self._validator_args = None
+        if validator is not None:
+            if type(validator) != FunctionType:
+                raise TypeError("validator must be a function")
+            self._validator = validator
+            if validator_args is not None:
+                self._validator_args = validator_args
         if not self.multi and default_multi is not None:
             raise ConfigError("a default_multi is set whereas multi is False"
                   " in option: {0}".format(name))
@@ -123,17 +132,29 @@ class Option(HiddenBaseType, DisabledBaseType):
         if self.multi == True:
             if default == None:
                 default = []
-            if not isinstance(default, list) or not self.validate(default):
+            if not isinstance(default, list):
                 raise ConfigError("invalid default value {0} "
                 "for option {1} : not list type".format(str(default), name))
+            if not self.validate(default, False):
+                raise ConfigError("invalid default value {0} "
+                "for option {1}".format(str(default), name))
         else:
-            if default != None and not self.validate(default):
+            if default != None and not self.validate(default, False):
                 raise ConfigError("invalid default value {0} "
                                          "for option {1}".format(str(default), name))
         self.default = default
         self.properties = [] # 'hidden', 'disabled'...
 
-    def validate(self, value):
+    def validate(self, value, validate=True):
+        """
+        :param value: the option's value
+        :param validate: if true enables ``self._validator`` validation
+        """
+        # customizing the validator
+        if validate and value is not None and self._validator is not None:
+            if not self._validator(value, **self._validator_args):
+                return False
+        # generic calculation
         if self.multi == False:
             # None allows the reset of the value
             if value != None:
@@ -235,7 +256,8 @@ class Option(HiddenBaseType, DisabledBaseType):
         :param who : is **not necessarily** a owner because it cannot be a list
         :type who: string """
         name = self._name
-        if not self.validate(value):
+        rootconfig = config._cfgimpl_get_toplevel()
+        if not self.validate(value, rootconfig._cfgimpl_validator):
             raise ConfigError('invalid value %s for option %s' % (value, name))
         if self.is_mandatory():
             # value shall not be '' for a mandatory option
@@ -253,7 +275,7 @@ class Option(HiddenBaseType, DisabledBaseType):
 
         if config.is_frozen() and self.is_frozen():
             raise TypeError('cannot change the value to %s for '
-               'option %s' % (str(value), name))
+               'option %s this option is frozen' % (str(value), name))
         apply_requires(self, config)
         if type(config._cfgimpl_values[name]) == Multi:
             config._cfgimpl_previous_values[name] = list(config._cfgimpl_values[name])
@@ -283,7 +305,8 @@ class ChoiceOption(Option):
 
     def __init__(self, name, doc, values, default=None, default_multi=None,
                  requires=None, mandatory=False, multi=False, callback=None,
-                 callback_params=None, open_values=False):
+                 callback_params=None, open_values=False, validator=None,
+                 validator_args={}):
         self.values = values
         if open_values not in [True, False]:
             raise ConfigError('Open_values must be a boolean for '
@@ -292,7 +315,8 @@ class ChoiceOption(Option):
         super(ChoiceOption, self).__init__(name, doc, default=default,
                         default_multi=default_multi, callback=callback,
                         callback_params=callback_params, requires=requires,
-                        multi=multi, mandatory=mandatory)
+                        multi=multi, mandatory=mandatory, validator=validator,
+                        validator_args=validator_args)
 
     def _validate(self, value):
         if not self.open_values:
@@ -306,14 +330,6 @@ class BoolOption(Option):
     def _validate(self, value):
         return isinstance(value, bool)
 
-# config level validator
-#    def setoption(self, config, value, who):
-#        name = self._name
-#        if value and self._validator is not None:
-#            toplevel = config._cfgimpl_get_toplevel()
-#            self._validator(toplevel)
-#        super(BoolOption, self).setoption(config, value, who)
-
 class IntOption(Option):
     opt_type = 'int'