values are in value objects now
[tiramisu.git] / tiramisu / config.py
index c2ac18e..514a985 100644 (file)
@@ -26,31 +26,50 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
     MandatoryError, MethodCallError, NoValueReturned)
 from tiramisu.option import (OptionDescription, Option, SymLinkOption,
     Multi, apply_requires)
-from tiramisu.setting import settings, groups, owners
+from tiramisu.setting import groups, owners, Setting
+from tiramisu.value import OptionValues
 
 # ____________________________________________________________
 class Config(object):
     "main configuration management entry"
     _cfgimpl_toplevel = None
 
-    def __init__(self, descr, parent=None):
+    def __init__(self, descr, parent=None, context=None):
         """ Configuration option management master class
         :param descr: describes the configuration schema
         :type descr: an instance of ``option.OptionDescription``
         :param parent: is None if the ``Config`` is root parent Config otherwise
         :type parent: ``Config``
+        :param context: the current root config
+        :type context: `Config`
         """
         self._cfgimpl_descr = descr
-        self._cfgimpl_value_owners = {}
         self._cfgimpl_parent = parent
-        "`Config()` indeed is in charge of the `Option()`'s values"
-        self._cfgimpl_values = {}
-        self._cfgimpl_previous_values = {}
+        if parent == None:
+            self._cfgimpl_settings = Setting()
+            self._cfgimpl_values = OptionValues()
+        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()
         self._cfgimpl_build()
 
+    def cfgimpl_get_settings(self):
+        return self._cfgimpl_context._cfgimpl_settings
+
+    def cfgimpl_set_settings(self, settings):
+        if not isinstance(settings, Setting):
+            raise ConfigError("setting not allowed")
+        self._cfgimpl_context._cfgimpl_settings = settings
+
     def _validate_duplicates(self, children):
         """duplicates Option names in the schema
         :type children: list of `Option` or `OptionDescription`
@@ -77,34 +96,35 @@ class Config(object):
                     childdef = Multi(copy(child.getdefault()), config=self,
                                      opt=child)
                     max_len_child = max(max_len_child, len(childdef))
-                    self._cfgimpl_values[child._name] = childdef
-                    self._cfgimpl_previous_values[child._name] = list(childdef)
+                    self._cfgimpl_context._cfgimpl_values[child] = childdef
+                    self._cfgimpl_context._cfgimpl_values.previous_values[child] = list(childdef)
                 else:
                     childdef = child.getdefault()
-                    self._cfgimpl_values[child._name] = childdef
-                    self._cfgimpl_previous_values[child._name] = childdef
+                    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):
                 self._validate_duplicates(child._children)
-                self._cfgimpl_values[child._name] = Config(child, parent=self)
-
-    def cfgimpl_update(self):
-        """dynamically adds `Option()` or `OptionDescription()`
-        """
-        # FIXME this is an update for new options in the schema only
-        #┬ásee the update_child() method of the descr object
-        for child in self._cfgimpl_descr._children:
-            if isinstance(child, Option):
-                if child._name not in self._cfgimpl_values:
-                    if child.is_multi():
-                        self._cfgimpl_values[child._name] = Multi(
-                                copy(child.getdefault()), config=self, opt=child)
-                    else:
-                        self._cfgimpl_values[child._name] = copy(child.getdefault())
-                    child.setowner(self, owners.default)
-            elif isinstance(child, OptionDescription):
-                if child._name not in self._cfgimpl_values:
-                    self._cfgimpl_values[child._name] = Config(child, parent=self)
+                self._cfgimpl_context._cfgimpl_values[child] = Config(child, parent=self,
+                                                context=self._cfgimpl_context)
+
+#    def cfgimpl_update(self):
+#        """dynamically adds `Option()` or `OptionDescription()`
+#        """
+#        # FIXME this is an update for new options in the schema only
+#        #┬ásee the update_child() method of the descr object
+#        for child in self._cfgimpl_descr._children:
+#            if isinstance(child, Option):
+#                if child._name not in self._cfgimpl_values:
+#                    if child.is_multi():
+#                        self._cfgimpl_values[child._name] = Multi(
+#                                copy(child.getdefault()), config=self, opt=child)
+#                    else:
+#                        self._cfgimpl_values[child._name] = copy(child.getdefault())
+#                    child.setowner(self, owners.default)
+#            elif isinstance(child, OptionDescription):
+#                if child._name not in self._cfgimpl_values:
+#                    self._cfgimpl_values[child._name] = Config(child, parent=self)
 
     # ____________________________________________________________
     # attribute methods
@@ -118,7 +138,8 @@ class Config(object):
             return setattr(homeconfig, name, value)
         if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
             self._validate(name, getattr(self._cfgimpl_descr, name))
-        self.setoption(name, value, settings.get_owner())
+        self.setoption(name, value,
+                            self._cfgimpl_context._cfgimpl_settings.get_owner())
 
     def _validate(self, name, opt_or_descr, permissive=False):
         "validation for the setattr and the getattr"
@@ -128,10 +149,10 @@ class Config(object):
             raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr)))
         properties = copy(opt_or_descr.properties)
         for proper in copy(properties):
-            if not settings.has_property(proper):
+            if not self._cfgimpl_context._cfgimpl_settings.has_property(proper):
                 properties.remove(proper)
         if permissive:
-            for perm in settings.permissive:
+            for perm in self._cfgimpl_context._cfgimpl_settings.permissive:
                 if perm in properties:
                     properties.remove(perm)
         if properties != []:
@@ -142,15 +163,15 @@ class Config(object):
 
     def _is_empty(self, opt):
         "convenience method to know if an option is empty"
-        if (not opt.is_multi() and self._cfgimpl_values[opt._name] == None) or \
-            (opt.is_multi() and (self._cfgimpl_values[opt._name] == [] or \
-                None in self._cfgimpl_values[opt._name])):
+        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 = settings.mandatory
+        mandatory = self._cfgimpl_context._cfgimpl_settings.mandatory
         if opt.is_mandatory() and mandatory:
             if self._is_empty(opt) and \
                     opt.is_empty_by_default():
@@ -160,10 +181,11 @@ class Config(object):
     def __getattr__(self, name):
         return self._getattr(name)
 
-    def fill_multi(self, name, result, use_default_multi=False, default_multi=None):
+    def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
         """fills a multi option with default and calculated values
         """
-        value = self._cfgimpl_values[name]
+        # FIXME C'EST ENCORE DU N'IMPORTE QUOI
+        value = self._cfgimpl_context._cfgimpl_values[opt]
         if not isinstance(result, list):
             _result = [result]
         else:
@@ -175,7 +197,7 @@ class Config(object):
         attribute notation mechanism for accessing the value of an option
         :param name: attribute name
         :param permissive: permissive doesn't raise some property error
-                          (see ``settings.permissive``)
+                          (see ``permissive``)
         :return: option's value if name is an option name, OptionDescription
                  otherwise
         """
@@ -189,7 +211,7 @@ class Config(object):
         if type(opt_or_descr) == SymLinkOption:
             rootconfig = self._cfgimpl_get_toplevel()
             return getattr(rootconfig, opt_or_descr.path)
-        if name not in self._cfgimpl_values:
+        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)
@@ -202,7 +224,7 @@ class Config(object):
         if not isinstance(opt_or_descr, OptionDescription):
             # options with callbacks
             if opt_or_descr.has_callback():
-                value = self._cfgimpl_values[name]
+                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):
@@ -214,7 +236,7 @@ class Config(object):
                     pass
                 else:
                     if opt_or_descr.is_multi():
-                        _result = self.fill_multi(name, result)
+                        _result = self.fill_multi(opt_or_descr, result)
                     else:
                         # this result **shall not** be a list
                         if isinstance(result, list):
@@ -222,22 +244,22 @@ class Config(object):
                                 ' for option {0} : shall not be a list'.format(name))
                         _result = result
                     if _result != None and not opt_or_descr.validate(_result,
-                                settings.validator):
+                                self._cfgimpl_context._cfgimpl_settings.validator):
                         raise ConfigError('invalid calculated value returned'
                             ' for option {0}'.format(name))
-                    self._cfgimpl_values[name] = _result
+                    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(name, value,
+                    value = self.fill_multi(opt_or_descr, value,
                                 use_default_multi=True,
                                 default_multi=opt_or_descr.getdefault_multi())
-                self._cfgimpl_values[name] = value
+                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_values[name]
+        value = self._cfgimpl_context._cfgimpl_values[opt_or_descr]
         return value
 
     def unwrap_from_name(self, name):
@@ -274,7 +296,7 @@ class Config(object):
         child = getattr(self._cfgimpl_descr, name)
         if type(child) != SymLinkOption:
             if who == None:
-                who = settings.owner
+                who = self._cfgimpl_context._cfgimpl_settings.owner
             if child.is_multi():
                 if not isinstance(who, owners.DefaultOwner):
                     if type(value) != Multi:
@@ -284,7 +306,7 @@ class Config(object):
                             raise ConfigError("invalid value for option:"
                                        " {0} that is set to multi".format(name))
                 else:
-                    value = self.fill_multi(name, child.getdefault(),
+                    value = self.fill_multi(child, child.getdefault(),
                                         use_default_multi=True,
                                         default_multi=child.getdefault_multi())
             if not isinstance(who, owners.Owner):
@@ -316,7 +338,8 @@ class Config(object):
                     pass
                 except Exception, e:
                     raise e # HiddenOptionError or DisabledOptionError
-                homeconfig.setoption(name, value, settings.get_owner())
+                homeconfig.setoption(name, value,
+                            self._cfgimpl_context._cfgimpl_settings.get_owner())
             elif len(candidates) > 1:
                 raise AmbigousOptionError(
                     'more than one option that ends with %s' % (key, ))
@@ -371,14 +394,15 @@ class Config(object):
             obj = obj._cfgimpl_parent
         return ".".join(subpath)
     # ______________________________________________________________________
-    def cfgimpl_previous_value(self, path):
-        "stores the previous value"
-        home, name = self._cfgimpl_get_home_by_path(path)
-        return home._cfgimpl_previous_values[name]
-
-    def get_previous_value(self, name):
-        "for the time being, only the previous Option's value is accessible"
-        return self._cfgimpl_previous_values[name]
+#    def cfgimpl_previous_value(self, path):
+#        "stores the previous value"
+#        home, name = self._cfgimpl_get_home_by_path(path)
+#        # FIXME  fucking name
+#        return home._cfgimpl_context._cfgimpl_values.previous_values[name]
+
+#    def get_previous_value(self, name):
+#        "for the time being, only the previous Option's value is accessible"
+#        return self._cfgimpl_context._cfgimpl_values.previous_values[name]
     # ______________________________________________________________________
     def add_warning(self, warning):
         "Config implements its own warning pile. Could be useful"
@@ -420,7 +444,7 @@ class Config(object):
         can be filtered by categories (families, or whatever).
         :param group_type: if defined, is an instance of `groups.GroupType`
                            or `groups.MasterGroupType` that lives in
-                           `settings.groups`
+                           `setting.groups`
 
         """
         if group_type is not None:
@@ -592,9 +616,10 @@ def mandatory_warnings(config):
     where no value has been set
 
     :returns: generator of mandatory Option's path
+    FIXME : CAREFULL : not multi-user
     """
-    mandatory = settings.mandatory
-    settings.mandatory = True
+    mandatory = config._cfgimpl_context._cfgimpl_settings.mandatory
+    config._cfgimpl_context._cfgimpl_settings.mandatory = True
     for path in config._cfgimpl_descr.getpaths(include_groups=True):
         try:
             value = config._getattr(path, permissive=True)
@@ -602,4 +627,4 @@ def mandatory_warnings(config):
             yield path
         except PropertiesOptionError:
             pass
-    settings.mandatory = mandatory
+    config._cfgimpl_context._cfgimpl_settings.mandatory = mandatory