diff --git a/tiramisu/config.py b/tiramisu/config.py index e97323e..0b8dbbe 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -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" diff --git a/tiramisu/option.py b/tiramisu/option.py index e69ea2e..5b865fc 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -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'