validation of the len of the multi in a group
authorgwen <gremond@cadoles.com>
Tue, 4 Dec 2012 11:06:26 +0000 (12:06 +0100)
committergwen <gremond@cadoles.com>
Tue, 4 Dec 2012 11:06:26 +0000 (12:06 +0100)
tiramisu/autolib.py
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py

index 55b655e..44b8934 100644 (file)
@@ -85,7 +85,12 @@ def carry_out_calculation(name, option, config):
                             params.append(value)
                         else:
                             tcp[key] = value
-            ret.append(calculate(name, callback, params, tcp))
+            calc = calculate(name, callback, params, tcp)
+            if isinstance(calc, list):
+                ret.extend(calc)
+            else:
+                ret.append(calc)
+
         return ret
     else:
         tcp = {}
index 5331c1e..13fb6e9 100644 (file)
@@ -25,8 +25,8 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
     AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
     MandatoryError, MethodCallError, NoValueReturned)
 from tiramisu.option import (OptionDescription, Option, SymLinkOption,
-    group_types, Multi, apply_requires)
-from tiramisu.setting import settings
+    Multi, apply_requires)
+from tiramisu.setting import settings, group_types
 
 # ____________________________________________________________
 class Config(object):
@@ -159,6 +159,52 @@ class Config(object):
     def __getattr__(self, name):
         return self._getattr(name)
 
+    def _get_master_len(self, slave_name):
+        try:
+            parent_cfg = self._cfgimpl_parent
+            if parent_cfg is None:
+                return None
+            master_name = parent_cfg._cfgimpl_descr.get_master_name()
+            master_value = getattr(parent_cfg, master_name)
+            return len(master_value)
+        except TypeError:
+            # in this case we just don't care about the len
+            return None
+
+    def _valid_len(self, slave_name, slave_value):
+        master_len = self._get_master_len(slave_name)
+        if master_len == None:
+            return True
+        if master_len != len(slave_value):
+            raise ValueError("invalid len for the group {0}"
+                    "in the option {1} ".format(master_name, slave_name))
+
+    def fill_multi(self, name, result, default_multi=None):
+        """fills a multi option with default and calculated values
+        """
+        value = self._cfgimpl_values[name]
+        master_len = self._get_master_len(name)
+        if not isinstance(result, list):
+            if master_len is None:
+                master_len = 1
+            # a list is built with the same len as the master
+            _result = []
+            for i in range(master_len):
+                _result.append(result)
+        elif default_multi != None:
+            if master_len != None:
+                slave_len = len(result)
+                if slave_len > master_len:
+                    raise ValueError("invalid value's len for"
+                            "the option: {1}".format(name))
+                if slave_len != master_len:
+                    delta_len = master_len - len(result)
+                    for i in range(delta_len):
+                        _result.append(default_multi)
+        else:
+            _result = result
+        return Multi(_result, value.config, value.child)
+
     def _getattr(self, name, permissive=False):
         """
         attribute notation mechanism for accessing the value of an option
@@ -195,6 +241,7 @@ class Config(object):
                 if (not opt_or_descr.is_frozen() or \
                         not opt_or_descr.is_forced_on_freeze()) and \
                         not opt_or_descr.is_default_owner(self):
+                    self._valid_len(name, value)
                     return value
                 try:
                     result = opt_or_descr.getcallback_value(
@@ -203,9 +250,7 @@ class Config(object):
                     pass
                 else:
                     if opt_or_descr.is_multi():
-                        if not isinstance(result, list):
-                            result = [result]
-                        _result = Multi(result, value.config, value.child)
+                        _result = self.fill_multi(name, result)
                     else:
                         # this result **shall not** be a list
                         if isinstance(result, list):
@@ -218,11 +263,17 @@ class Config(object):
                             ' for option {0}'.format(name))
                     self._cfgimpl_values[name] = _result
                     opt_or_descr.setowner(self, 'default')
+            # frozen and force default
             if not opt_or_descr.has_callback() and opt_or_descr.is_forced_on_freeze():
-                return opt_or_descr.getdefault()
+                value = opt_or_descr.getdefault()
+                if opt_or_descr.is_multi():
+                    value = self.fill_multi(name, result, opt_or_descr.getdefault_multi())
+                self._cfgimpl_values[name] = value
+                opt_or_descr.setowner(self, 'default')
             self._test_mandatory(name, opt_or_descr)
-            # frozen and force default
-        return self._cfgimpl_values[name]
+        value = self._cfgimpl_values[name]
+        self._valid_len(name, value)
+        return value
 
     def unwrap_from_name(self, name):
         """convenience method to extract and Option() object from the Config()
@@ -375,10 +426,8 @@ class Config(object):
 
     def __eq__(self, other):
         "Config comparison"
-        if not isinstance(other, Config):
+        if not isinstance(other, OptionDescription):
             return False
-        print self.getkey()
-        print other.getkey()
         return self.getkey() == other.getkey()
 
     def __ne__(self, other):
index 1af3a81..007bf84 100644 (file)
@@ -26,7 +26,7 @@ from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
     RequiresError, RequirementRecursionError, MandatoryError,
     PropertiesOptionError)
 from tiramisu.autolib import carry_out_calculation
-from tiramisu.setting import settings
+from tiramisu.setting import settings, group_types, groups_has_master
 
 requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')]
 
@@ -37,15 +37,6 @@ for act1, act2 in requires_actions:
     reverse_actions[act1] = act2
     reverse_actions[act2] = act1
 # ____________________________________________________________
-# OptionDescription authorized group_type values
-"""
-Three available group_types : `default`, `family`, `group` and
-`master` (for master~slave group type). Notice that for a
-master~slave group, the name of the group and the name of the
-master option are identical.
-"""
-group_types = ['default', 'family', 'group', 'master']
-# ____________________________________________________________
 # multi types
 
 class Multi(list):
@@ -416,6 +407,9 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
         self._requires = requires
         self._build()
         self.properties = [] # 'hidden', 'disabled'...
+        # if this group is a master group, master is set
+        # to the master option name
+        self.master = None
 
     def getdoc(self):
         return self.doc
@@ -466,16 +460,33 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
                 paths.append('.'.join(currpath + [attr]))
         return paths
     # ____________________________________________________________
-    def set_group_type(self, group_type):
+    def set_group_type(self, group_type, master=None):
         ":param group_type: string in group_types"
         if group_type in group_types:
             self.group_type = group_type
+            if group_type in groups_has_master:
+                if master is None:
+                    raise ConfigError('this group type ({0}) needs a master '
+                            'for OptionDescription {1}'.format(group_type,
+                                self._name))
+            else:
+                if master is not None:
+                    raise ConfigError("this group type ({0}) doesn't need a "
+                            "master for OptionDescription {1}".format(
+                                group_type, self._name))
+            self.master = master
         else:
             raise ConfigError('not allowed value for group_type : {0}'.format(
                               group_type))
 
     def get_group_type(self):
         return self.group_type
+
+    def get_master_name(self):
+        if self.master is None:
+            raise TypeError('get_master_name() shall not be called in case of'
+                'non-master OptionDescription')
+        return self.master
     # ____________________________________________________________
     "actions API"
     def hide(self):
index b8275f0..c2140a9 100644 (file)
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
 
+# available group_type values
+_group_name = ('default', 'family', 'group')
+groups_has_master = ('group', )
+class Group(str): pass
+group_types = tuple(Group(i) for i in _group_name)
+
 class Setting():
     "``Config()``'s configuration options"
     # properties attribute: the name of a property enables this property