diff --git a/tiramisu/config.py b/tiramisu/config.py index c31ada8..961d5dc 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -27,7 +27,8 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError, from tiramisu.option import (OptionDescription, Option, SymLinkOption, apply_requires) from tiramisu.setting import groups, owners, Setting -from tiramisu.value import OptionValues +from tiramisu.value import Values + # ____________________________________________________________ class Config(object): @@ -53,9 +54,9 @@ class Config(object): self._cfgimpl_context = self else: self._cfgimpl_context = context - if parent == None: + if parent is None: self._cfgimpl_settings = Setting() - self._cfgimpl_values = OptionValues(self._cfgimpl_context) + self._cfgimpl_values = Values(self._cfgimpl_context) else: if context is None: raise ConfigError("cannot find a value for this config") @@ -95,7 +96,7 @@ class Config(object): if self._cfgimpl_descr.group_type == groups.master: mastername = self._cfgimpl_descr._name masteropt = getattr(self._cfgimpl_descr, mastername) - self._cfgimpl_values.master_groups[masteropt] = [] + self._cfgimpl_values.masters[masteropt] = [] for child in self._cfgimpl_descr._children: if isinstance(child, OptionDescription): @@ -104,11 +105,9 @@ class Config(object): context=self._cfgimpl_context) if (self._cfgimpl_descr.group_type == groups.master and child != masteropt): - self._cfgimpl_values.master_groups[child] = [] - self._cfgimpl_values.master_groups[masteropt].append(child) + self._cfgimpl_values.slaves[child] = masteropt + self._cfgimpl_values.masters[masteropt].append(child) - if self._cfgimpl_descr.group_type == groups.master: - print self._cfgimpl_values.master_groups # ____________________________________________________________ # attribute methods def __setattr__(self, name, value): @@ -241,7 +240,7 @@ class Config(object): except MandatoryError: pass except Exception, e: - raise e # HiddenOptionError or DisabledOptionError + raise e # HiddenOptionError or DisabledOptionError homeconfig.setoption(name, value) elif len(candidates) > 1: raise AmbigousOptionError( @@ -249,7 +248,7 @@ class Config(object): else: raise NoMatchingOptionFound( 'there is no option that matches %s' - ' or the option is hidden or disabled'% (key, )) + ' or the option is hidden or disabled' % (key, )) def get(self, name): """ @@ -379,7 +378,6 @@ class Config(object): __repr__ = __str__ - def getpaths(self, include_groups=False, allpaths=False, mandatory=False): """returns a list of all paths in self, recursively, taking care of the context of properties (hidden/disabled) @@ -438,7 +436,7 @@ class Config(object): value = getattr(self, path) if value == byvalue: return True - except: # a property restricts the access of the value + except: # a property restricts the access of the value pass return False def _filter_by_type(): @@ -497,6 +495,7 @@ class Config(object): """ return self._find(bytype, byname, byvalue, byattrs, first=True) + def make_dict(config, flatten=False): """export the whole config into a `dict` :returns: dict of Option's name (or path) and values""" @@ -511,10 +510,11 @@ def make_dict(config, flatten=False): value = getattr(config, path) pathsvalues.append((pathname, value)) except: - pass # this just a hidden or disabled option + pass # this just a hidden or disabled option options = dict(pathsvalues) return options + def mandatory_warnings(config): """convenience function to trace Options that are mandatory and where no value has been set diff --git a/tiramisu/error.py b/tiramisu/error.py index 0880e0f..fae89a7 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -25,3 +25,5 @@ class NoValueReturned(Exception): pass class OptionValueError(Exception): pass +class MultiTypeError(Exception): + pass diff --git a/tiramisu/value.py b/tiramisu/value.py index d6182f6..07236d5 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -21,9 +21,9 @@ # the whole pypy projet is under MIT licence # ____________________________________________________________ from tiramisu.error import NoValueReturned, MandatoryError -from tiramisu.setting import owners +from tiramisu.setting import owners, multitypes -class OptionValues(object): +class Values(object): def __init__(self, context): """ Initializes the values's dict. @@ -34,17 +34,25 @@ class OptionValues(object): "Config's root indeed is in charge of the `Option()`'s values" self.values = {} self.previous_values = {} - self.master_groups = {} + self.masters = {} + self.slaves = {} self.context = context def _get_value(self, opt): "special case for the multis: they never return None" - if opt.is_multi(): - if opt not in self.values: + if opt not in self.values: + if opt.is_multi(): + if opt in self.slaves: + # slave + multitype = multitypes.slave + elif opt in self.masters: + # master + multitype = multitypes.master # FIXME : default value for a multi, we shall work on groups - return Multi(opt.getdefault(), self.context, opt) - else: - if opt not in self.values: + else: + multitype = multitypes.default + return Multi(opt.getdefault(), self.context, opt, multitype) + else: return opt.getdefault() return self.values[opt] @@ -128,6 +136,21 @@ class OptionValues(object): return value def __setitem__(self, opt, value): + if opt in self.masters: + masterlen = len(value) + for slave in masters[opt]: + if len(self._get_value(slave)) != masterlen: + raise MultiTypeError("invalid len for the slave: {0}" + "which has {1} as master".format(slave._name, + master._name)) + elif opt in self.slaves: + if len(self._get_value(self.slaves[opt])) != len(value): + raise MultiTypeError("invalid len for the slave: {0}" + "which has {1} as master".format(slave._name, + master._name)) + self.setitem(opt, value) + + def setitem(self, opt, value): self.set_previous_value(opt) self.values[opt] = value self.setowner(opt, self.context._cfgimpl_settings.getowner()) @@ -149,7 +172,7 @@ class OptionValues(object): class Multi(list): """multi options values container that support item notation for the values of multi options""" - def __init__(self, lst, context, opt, multitype=settings.multitypes.default): + def __init__(self, lst, context, opt, multitype): """ :param lst: the Multi wraps a list value :param context: the home config that has the settings and the values @@ -160,40 +183,48 @@ class Multi(list): self.values = context._cfgimpl_values self.multitype = multitype super(Multi, self).__init__(lst) - + if multitype == multitypes.master: + self.slaves = context._cfgimpl_values.masters[opt] + else: + self.slaves = None def __setitem__(self, key, value): - self._setvalue(value, key, who=self.settings.getowner()) + self._validate(value) + self.values[self.opt] = self + super(Multi, self).__setitem__(key, value) - def append(self, value): + def append(self, value, force=False): """the list value can be updated (appened) only if the option is a master """ - self._setvalue(value, who=self.settings.getowner(self.opt)) + if not force: + if self.multitype == multitypes.slave: + raise MultiTypeError("cannot append a value on a multi option {0}" + " wich is a slave".format(self.opt._name)) + elif self.multitype == multitypes.master: + for slave in self.slaves: + self.values[slave].append(None, force=True) + self._validate(value) + self.values.setitem(self.opt, self) + super(Multi, self).append(value) - def _setvalue(self, value, key=None, who=None): - if value != None: - if not self.opt._validate(value): - raise ConfigError("invalid value {0} " + def _validate(self, value): + if value != None and not self.opt._validate(value): + raise ConfigError("invalid value {0} " "for option {1}".format(str(value), self.opt._name)) - oldvalue = list(self) - if key is None: - super(Multi, self).append(value) - else: - super(Multi, self).__setitem__(key, value) - if who != None: - if not isinstance(who, owners.Owner): - raise TypeError("invalid owner {0} for the value {1}".format( - str(who), str(value))) - self.values.setowner(self.opt, getattr(owners, who)) - self.values.previous_values[self.opt] = oldvalue - def pop(self, key): + def pop(self, key, force=False): """the list value can be updated (poped) only if the option is a master :param key: index of the element to pop :return: the requested element """ - self.values.setowner(opt, self.settings.get_owner()) - self.values.previous_values[self.opt] = list(self) + if not force: + if self.multitype == multitypes.slave: + raise MultiTypeError("cannot append a value on a multi option {0}" + " wich is a slave".format(self.opt._name)) + elif self.multitype == multitypes.master: + for slave in self.slaves: + self.values[slave].pop(key, force=True) + self.values.setitem(self.opt, self) return super(Multi, self).pop(key)