refactoring, the values are in an OptionValues object
[tiramisu.git] / tiramisu / config.py
index 514a985..47ee6dc 100644 (file)
@@ -25,9 +25,9 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
     AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
     MandatoryError, MethodCallError, NoValueReturned)
 from tiramisu.option import (OptionDescription, Option, SymLinkOption,
-    Multi, apply_requires)
+    apply_requires)
 from tiramisu.setting import groups, owners, Setting
-from tiramisu.value import OptionValues
+from tiramisu.value import OptionValues, Multi
 
 # ____________________________________________________________
 class Config(object):
@@ -43,20 +43,23 @@ class Config(object):
         :param context: the current root config
         :type context: `Config`
         """
+        # main option description
         self._cfgimpl_descr = descr
+        # sub option descriptions
+        self._cfgimpl_subconfigs = {}
         self._cfgimpl_parent = parent
+        if context is None:
+            self._cfgimpl_context = self
+        else:
+            self._cfgimpl_context = context
         if parent == None:
             self._cfgimpl_settings = Setting()
-            self._cfgimpl_values = OptionValues()
+            self._cfgimpl_values = OptionValues(self._cfgimpl_context)
         else:
             if context is None:
                 raise ConfigError("cannot find a value for this config")
             self._cfgimpl_settings = None
             self._cfgimpl_values = None
-        if context is None:
-            self._cfgimpl_context = self
-        else:
-            self._cfgimpl_context = context
         "warnings are a great idea, let's make up a better use of it"
         self._cfgimpl_warnings = []
         self._cfgimpl_toplevel = self._cfgimpl_get_toplevel()
@@ -91,21 +94,9 @@ class Config(object):
         #max len for a master/slave group
         max_len_child = 0
         for child in self._cfgimpl_descr._children:
-            if isinstance(child, Option):
-                if child.is_multi():
-                    childdef = Multi(copy(child.getdefault()), config=self,
-                                     opt=child)
-                    max_len_child = max(max_len_child, len(childdef))
-                    self._cfgimpl_context._cfgimpl_values[child] = childdef
-                    self._cfgimpl_context._cfgimpl_values.previous_values[child] = list(childdef)
-                else:
-                    childdef = child.getdefault()
-                    self._cfgimpl_context._cfgimpl_values[child] = childdef
-                    self._cfgimpl_context._cfgimpl_values.previous_values[child] = childdef
-                child.setowner(self, owners.default)
-            elif isinstance(child, OptionDescription):
+            if isinstance(child, OptionDescription):
                 self._validate_duplicates(child._children)
-                self._cfgimpl_context._cfgimpl_values[child] = Config(child, parent=self,
+                self._cfgimpl_subconfigs[child] = Config(child, parent=self,
                                                 context=self._cfgimpl_context)
 
 #    def cfgimpl_update(self):
@@ -139,7 +130,7 @@ class Config(object):
         if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
             self._validate(name, getattr(self._cfgimpl_descr, name))
         self.setoption(name, value,
-                            self._cfgimpl_context._cfgimpl_settings.get_owner())
+                            self._cfgimpl_context._cfgimpl_settings.getowner())
 
     def _validate(self, name, opt_or_descr, permissive=False):
         "validation for the setattr and the getattr"
@@ -161,23 +152,6 @@ class Config(object):
                     " {1}".format(name, str(properties)),
                     properties)
 
-    def _is_empty(self, opt):
-        "convenience method to know if an option is empty"
-        if (not opt.is_multi() and self._cfgimpl_context._cfgimpl_values[opt] == None) or \
-            (opt.is_multi() and (self._cfgimpl_context._cfgimpl_values[opt] == [] or \
-                None in self._cfgimpl_context._cfgimpl_values[opt])):
-            return True
-        return False
-
-    def _test_mandatory(self, path, opt):
-        # mandatory options
-        mandatory = self._cfgimpl_context._cfgimpl_settings.mandatory
-        if opt.is_mandatory() and mandatory:
-            if self._is_empty(opt) and \
-                    opt.is_empty_by_default():
-                raise MandatoryError("option: {0} is mandatory "
-                                      "and shall have a value".format(path))
-
     def __getattr__(self, name):
         return self._getattr(name)
 
@@ -185,12 +159,11 @@ class Config(object):
         """fills a multi option with default and calculated values
         """
         # FIXME C'EST ENCORE DU N'IMPORTE QUOI
-        value = self._cfgimpl_context._cfgimpl_values[opt]
         if not isinstance(result, list):
             _result = [result]
         else:
             _result = result
-        return Multi(_result, value.config, opt=value.opt)
+        return Multi(_result, self._cfgimpl_context, opt)
 
     def _getattr(self, name, permissive=False):
         """
@@ -211,56 +184,18 @@ class Config(object):
         if type(opt_or_descr) == SymLinkOption:
             rootconfig = self._cfgimpl_get_toplevel()
             return getattr(rootconfig, opt_or_descr.path)
-        if opt_or_descr not in self._cfgimpl_context._cfgimpl_values:
-            raise AttributeError("%s object has no attribute %s" %
-                                 (self.__class__, name))
+
         self._validate(name, opt_or_descr, permissive)
+        if isinstance(opt_or_descr, OptionDescription):
+            if opt_or_descr not in self._cfgimpl_subconfigs:
+                raise AttributeError("%s with name %s object has no attribute %s" %
+                                 (self.__class__, opt_or_descr._name, name))
+            return self._cfgimpl_subconfigs[opt_or_descr]
         # special attributes
         if name.startswith('_cfgimpl_'):
             # if it were in __dict__ it would have been found already
             return self.__dict__[name]
-            raise AttributeError("%s object has no attribute %s" %
-                                 (self.__class__, name))
-        if not isinstance(opt_or_descr, OptionDescription):
-            # options with callbacks
-            if opt_or_descr.has_callback():
-                value = self._cfgimpl_context._cfgimpl_values[opt_or_descr]
-                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):
-                    return value
-                try:
-                    result = opt_or_descr.getcallback_value(
-                            self._cfgimpl_get_toplevel())
-                except NoValueReturned, err:
-                    pass
-                else:
-                    if opt_or_descr.is_multi():
-                        _result = self.fill_multi(opt_or_descr, result)
-                    else:
-                        # this result **shall not** be a list
-                        if isinstance(result, list):
-                            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,
-                                self._cfgimpl_context._cfgimpl_settings.validator):
-                        raise ConfigError('invalid calculated value returned'
-                            ' for option {0}'.format(name))
-                    self._cfgimpl_context._cfgimpl_values[opt_or_descr] = _result
-                    opt_or_descr.setowner(self, owners.default)
-            # frozen and force default
-            if not opt_or_descr.has_callback() and opt_or_descr.is_forced_on_freeze():
-                value = opt_or_descr.getdefault()
-                if opt_or_descr.is_multi():
-                    value = self.fill_multi(opt_or_descr, value,
-                                use_default_multi=True,
-                                default_multi=opt_or_descr.getdefault_multi())
-                self._cfgimpl_context._cfgimpl_values[opt_or_descr] = value
-                opt_or_descr.setowner(self, owners.default)
-            self._test_mandatory(name, opt_or_descr)
-        value = self._cfgimpl_context._cfgimpl_values[opt_or_descr]
-        return value
+        return self._cfgimpl_context._cfgimpl_values[opt_or_descr]
 
     def unwrap_from_name(self, name):
         """convenience method to extract and Option() object from the Config()
@@ -301,7 +236,7 @@ class Config(object):
                 if not isinstance(who, owners.DefaultOwner):
                     if type(value) != Multi:
                         if type(value) == list:
-                            value = Multi(value, self, child)
+                            value = Multi(value, self._cfgimpl_context, child)
                         else:
                             raise ConfigError("invalid value for option:"
                                        " {0} that is set to multi".format(name))
@@ -339,7 +274,7 @@ class Config(object):
                 except Exception, e:
                     raise e # HiddenOptionError or DisabledOptionError
                 homeconfig.setoption(name, value,
-                            self._cfgimpl_context._cfgimpl_settings.get_owner())
+                            self._cfgimpl_context._cfgimpl_settings.getowner())
             elif len(candidates) > 1:
                 raise AmbigousOptionError(
                     'more than one option that ends with %s' % (key, ))