diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 42fcf7f..8f321cf 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -42,12 +42,13 @@ def carry_out_calculation(name, option, config): if type(value) == tuple: path, check_disabled = value try: - opt_value = getattr(config, path) + #opt_value = getattr(config, path) + opt_value = config._getattr(path, permissive=True) opt = config.unwrap_from_path(path) - except PropertiesOptionError, e: + except PropertiesOptionError, err: if check_disabled: continue - raise PropertiesOptionError(e) + raise PropertiesOptionError(err, err.proptype) is_multi = opt.is_multi() if is_multi: if opt_value != None: diff --git a/tiramisu/config.py b/tiramisu/config.py index bd0b553..72a0312 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -34,6 +34,7 @@ default_owner = 'user' class Config(object): "properties attribute: the name of a property enables this property" _cfgimpl_properties = ['hidden', 'disabled'] + _cfgimpl_permissive = [] "mandatory means: a mandatory option has to have a value that is not None" _cfgimpl_mandatory = True _cfgimpl_frozen = True @@ -100,6 +101,11 @@ class Config(object): self._cfgimpl_values[child._name] = Config(child, parent=self) self.override(overrides) + def cfgimpl_set_permissive(self, permissive): + if not isinstance(permissive, list): + raise TypeError('permissive must be a list') + self._cfgimpl_permissive = permissive + def cfgimpl_update(self): """dynamically adds `Option()` or `OptionDescription()` """ @@ -175,7 +181,7 @@ class Config(object): self._validate(name, getattr(self._cfgimpl_descr, name)) self.setoption(name, value, self._cfgimpl_owner) - def _validate(self, name, opt_or_descr): + def _validate(self, name, opt_or_descr, permissive=False): "validation for the setattr and the getattr" apply_requires(opt_or_descr, self) if not isinstance(opt_or_descr, Option) and \ @@ -185,6 +191,10 @@ class Config(object): for proper in properties: if not self._cfgimpl_toplevel._cfgimpl_has_property(proper): properties.remove(proper) + if permissive: + for perm in self._cfgimpl_toplevel._cfgimpl_permissive: + if perm in properties: + properties.remove(perm) if properties != []: raise PropertiesOptionError("trying to access" " to an option named: {0} with properties" @@ -200,12 +210,15 @@ class Config(object): return False def __getattr__(self, name): + return self._getattr(name) + + def _getattr(self, name, permissive=False): "attribute notation mechanism for accessing the value of an option" # attribute access by passing a path, # for instance getattr(self, "creole.general.family.adresse_ip_eth0") if '.' in name: homeconfig, name = self._cfgimpl_get_home_by_path(name) - return getattr(homeconfig, name) + return homeconfig._getattr(name, permissive) opt_or_descr = getattr(self._cfgimpl_descr, name) # symlink options if type(opt_or_descr) == SymLinkOption: @@ -213,7 +226,7 @@ class Config(object): if name not in self._cfgimpl_values: raise AttributeError("%s object has no attribute %s" % (self.__class__, name)) - self._validate(name, opt_or_descr) + self._validate(name, opt_or_descr, permissive) # special attributes if name.startswith('_cfgimpl_'): # if it were in __dict__ it would have been found already @@ -271,9 +284,8 @@ class Config(object): raise MandatoryError("option: {0} is mandatory " "and shall have a value".format(name)) # frozen and force default - if opt_or_descr.is_forced_on_freeze(): + if not opt_or_descr.has_callback() and opt_or_descr.is_forced_on_freeze(): return opt_or_descr.getdefault() - return self._cfgimpl_values[name] def unwrap_from_name(self, name): diff --git a/tiramisu/error.py b/tiramisu/error.py index d00d2d7..ba4a289 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -7,7 +7,7 @@ class ConfigError(Exception): class ConflictConfigError(ConfigError): pass class PropertiesOptionError(AttributeError): - def __init__(self, msg, proptype=None): + def __init__(self, msg, proptype): self.proptype = proptype super(PropertiesOptionError, self).__init__(msg) diff --git a/tiramisu/option.py b/tiramisu/option.py index ed1cbfb..abeb991 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -22,7 +22,8 @@ # ____________________________________________________________ from tiramisu.basetype import HiddenBaseType, DisabledBaseType from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError, - RequiresError, RequirementRecursionError, MandatoryError) + RequiresError, RequirementRecursionError, MandatoryError, + PropertiesOptionError) from tiramisu.autolib import carry_out_calculation requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')] @@ -195,8 +196,7 @@ class Option(HiddenBaseType, DisabledBaseType): def getcallback_value(self, config): return carry_out_calculation(self._name, - option=self, - config=config) + option=self, config=config) def getcallback_params(self): "if a callback has been defined, returns his arity" @@ -539,15 +539,18 @@ def apply_requires(opt, config): "imbrication detected for option: '{0}' " "with requirement on: '{1}'".format(path, name)) homeconfig, shortname = rootconfig._cfgimpl_get_home_by_path(name) - if shortname in homeconfig._cfgimpl_values: - value = homeconfig._cfgimpl_values[shortname] - if value == expected: - getattr(opt, action)() #.hide() or show() or... - # FIXME generic programming opt.property_launch(action, False) - matches = True - else: # option doesn't exist ! should not happen... + try: + value = homeconfig._getattr(shortname, permissive=True) + except PropertiesOptionError, err: + raise NotFoundError("required option has property error: " + "{0} {1}".format(name, err.proptype)) + except Exception, err: raise NotFoundError("required option not found: " "{0}".format(name)) + if value == expected: + getattr(opt, action)() #.hide() or show() or... + # FIXME generic programming opt.property_launch(action, False) + matches = True # no callback has been triggered, then just reverse the action if not matches: getattr(opt, reverse_actions[action])()