refactoring values
authorgwen <gremond@cadoles.com>
Thu, 21 Feb 2013 16:07:00 +0000 (17:07 +0100)
committergwen <gremond@cadoles.com>
Thu, 21 Feb 2013 16:07:00 +0000 (17:07 +0100)
test/test_parsing_group.py
tiramisu/config.py
tiramisu/error.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/value.py

index b726c2b..84eb57d 100644 (file)
@@ -89,6 +89,14 @@ def test_groups_with_master():
     interface1.set_group_type(groups.master)
     assert interface1.get_group_type() == groups.master
 
+def test_groups_with_master_in_config():
+    ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
+    netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
+    interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
+    interface1.set_group_type(groups.master)
+    cfg = Config(interface1)
+    assert interface1.get_group_type() == groups.master
+
 def test_allowed_groups():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
@@ -108,7 +116,7 @@ def test_sub_group_in_master_group():
     invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
     raises(ConfigError, "invalid_group.set_group_type(groups.master)")
 
-def test_group_has_always_multis():
+def test_group_always_has_multis():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
     group = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
index ddead27..c31ada8 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "pretty small and local configuration management tool"
-# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors)
+# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@ 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, Multi
+from tiramisu.value import OptionValues
 
 # ____________________________________________________________
 class Config(object):
@@ -92,32 +92,23 @@ class Config(object):
         - settles various default values for options
         """
         self._validate_duplicates(self._cfgimpl_descr._children)
-        #max len for a master/slave group
-        max_len_child = 0
+        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] = []
+
         for child in self._cfgimpl_descr._children:
             if isinstance(child, OptionDescription):
                 self._validate_duplicates(child._children)
                 self._cfgimpl_subconfigs[child] = Config(child, parent=self,
                                                 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)
 
-#    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)
-
+        if self._cfgimpl_descr.group_type == groups.master:
+            print self._cfgimpl_values.master_groups
     # ____________________________________________________________
     # attribute methods
     def __setattr__(self, name, value):
@@ -130,8 +121,7 @@ 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,
-                            self._cfgimpl_context._cfgimpl_settings.getowner())
+        self.setoption(name, value)
 
     def _validate(self, name, opt_or_descr, permissive=False):
         "validation for the setattr and the getattr"
@@ -156,15 +146,15 @@ class Config(object):
     def __getattr__(self, name):
         return self._getattr(name)
 
-    def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
-        """fills a multi option with default and calculated values
-        """
-        # FIXME C'EST ENCORE DU N'IMPORTE QUOI
-        if not isinstance(result, list):
-            _result = [result]
-        else:
-            _result = result
-        return Multi(_result, self._cfgimpl_context, opt)
+#    def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
+#        """fills a multi option with default and calculated values
+#        """
+#        # FIXME C'EST ENCORE DU N'IMPORTE QUOI
+#        if not isinstance(result, list):
+#            _result = [result]
+#        else:
+#            _result = result
+#        return Multi(_result, self._cfgimpl_context, opt)
 
     def _getattr(self, name, permissive=False):
         """
@@ -227,40 +217,16 @@ class Config(object):
     def setoption(self, name, value, who=None):
         """effectively modifies the value of an Option()
         (typically called by the __setattr__)
-        
-        :param who: an object that lives in `setting.owners`
         """
         child = getattr(self._cfgimpl_descr, name)
-        if type(child) != SymLinkOption:
-            if who == None:
-                who = self._cfgimpl_context._cfgimpl_settings.owner
-            if child.is_multi():
-                if not isinstance(who, owners.DefaultOwner):
-                    if type(value) != Multi:
-                        if type(value) == list:
-                            value = Multi(value, self._cfgimpl_context, child)
-                        else:
-                            raise ConfigError("invalid value for option:"
-                                       " {0} that is set to multi".format(name))
-                else:
-                    value = self.fill_multi(child, child.getdefault(),
-                                        use_default_multi=True,
-                                        default_multi=child.getdefault_multi())
-            if not isinstance(who, owners.Owner):
-                raise TypeError("invalid owner [{0}] for option: {1}".format(
-                                str(who), name))
-            child.setoption(self, value)
-            child.setowner(self, who)
-        else:
-            homeconfig = self._cfgimpl_get_toplevel()
-            child.setoption(homeconfig, value)
+        child.setoption(self, value)
 
     def set(self, **kwargs):
         """
         do what I mean"-interface to option setting. Searches all paths
         starting from that config for matches of the optional arguments
         and sets the found option if the match is not ambiguous.
-        
+
         :param kwargs: dict of name strings to values.
         """
         all_paths = [p.split(".") for p in self.getpaths(allpaths=True)]
@@ -276,8 +242,7 @@ class Config(object):
                     pass
                 except Exception, e:
                     raise e # HiddenOptionError or DisabledOptionError
-                homeconfig.setoption(name, value,
-                            self._cfgimpl_context._cfgimpl_settings.getowner())
+                homeconfig.setoption(name, value)
             elif len(candidates) > 1:
                 raise AmbigousOptionError(
                     'more than one option that ends with %s' % (key, ))
@@ -380,7 +345,7 @@ class Config(object):
         """iteration on groups objects only.
         All groups are returned if `group_type` is `None`, otherwise the groups
         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
                            `setting.groups`
index ba4a289..0880e0f 100644 (file)
@@ -23,3 +23,5 @@ class MandatoryError(Exception):
     pass
 class NoValueReturned(Exception):
     pass
+class OptionValueError(Exception):
+    pass
index ca949af..3fcf808 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "option types and option description for the configuration management"
-# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors)
+# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -27,7 +27,6 @@ from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
     PropertiesOptionError)
 from tiramisu.autolib import carry_out_calculation
 from tiramisu.setting import groups, owners
-from tiramisu.value import Multi
 
 requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')]
 
@@ -217,7 +216,7 @@ class Option(HiddenBaseType, DisabledBaseType):
     def reset(self, config):
         """resets the default value and owner
         """
-        config.setoption(self._name, self.getdefault(), owners.default)
+        config._cfgimpl_context._cfgimpl_values.reset(self)
 
     def is_default_owner(self, config):
         """
@@ -241,9 +240,9 @@ class Option(HiddenBaseType, DisabledBaseType):
             # so '' is considered as being None
             if not self.is_multi() and value == '':
                 value = None
-            if self.is_multi() and '' in value:
-                value = Multi([{'': None}.get(i, i) for i in value],
-                                config._cfgimpl_context, self)
+#            if self.is_multi() and '' in value:
+#                value = Multi([{'': None}.get(i, i) for i in value],
+#                                config._cfgimpl_context, self)
             if config._cfgimpl_context._cfgimpl_settings.is_mandatory() \
                 and ((self.is_multi() and value == []) or \
                 (not self.is_multi() and value is None)):
@@ -261,10 +260,6 @@ class Option(HiddenBaseType, DisabledBaseType):
             raise TypeError('cannot change the value to %s for '
                'option %s this option is frozen' % (str(value), name))
         apply_requires(self, config)
-#        if type(config._cfgimpl_context._cfgimpl_values[self]) == Multi:
-#            config._cfgimpl_context._cfgimpl_values.previous_values[self] = list(config._cfgimpl_context._cfgimpl_values[self])
-#        else:
-#            config._cfgimpl_context._cfgimpl_values.previous_values[self] = config._cfgimpl_context._cfgimpl_values[self]
         config._cfgimpl_context._cfgimpl_values[self] = value
 
     def getkey(self, value):
@@ -341,7 +336,7 @@ class SymLinkOption(object):
         self.opt = opt
 
     def setoption(self, config, value):
-        setattr(config, self.path, value)
+        setattr(config._cfgimpl_get_toplevel(), self.path, value)
 
     def __getattr__(self, name):
         if name in ('_name', 'path', 'opt', 'setoption'):
index ae9fe94..23d2157 100644 (file)
@@ -95,6 +95,26 @@ def populate_owners():
 
 # names are in the module now
 populate_owners()
+
+class MultiTypeModule(_const):
+    class MultiType(str):
+        pass
+    class DefaultMultiType(MultiType):
+        pass
+    class MasterMultiType(MultiType):
+        pass
+    class SlaveMultiType(MultiType):
+        pass
+
+multitypes = MultiTypeModule()
+
+def populate_multitypes():
+    setattr(multitypes, 'default', multitypes.DefaultMultiType('default'))
+    setattr(multitypes, 'master', multitypes.MasterMultiType('master'))
+    setattr(multitypes, 'slave', multitypes.SlaveMultiType('slave'))
+
+populate_multitypes()
+
 #____________________________________________________________
 class Setting():
     "``Config()``'s configuration options"
index 3abdccd..d6182f6 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "takes care of the option's values and multi values"
-# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors)
+# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -25,10 +25,16 @@ from tiramisu.setting import owners
 
 class OptionValues(object):
     def __init__(self, context):
+        """
+        Initializes the values's dict.
+
+        :param context: the context is the home config's values and properties
+        """
         self.owners = {}
         "Config's root indeed is in charge of the `Option()`'s values"
         self.values = {}
         self.previous_values = {}
+        self.master_groups = {}
         self.context = context
 
     def _get_value(self, opt):
@@ -42,24 +48,42 @@ class OptionValues(object):
                 return opt.getdefault()
         return self.values[opt]
 
-    def _is_empty(self, opt):
+    def reset(self, opt):
+        if opt in self.values:
+            self.set_previous_value(opt)
+            del(self.values[opt])
+        self.setowner(opt, owners.default)
+
+    def set_previous_value(self, opt):
+        if opt in self.values:
+            old_value = self.values[opt]
+        else:
+            old_value = None
+        if type(old_value) == Multi:
+           self.previous_values[opt] = list(old_value)
+        else:
+           self.previous_values[opt] = old_value
+
+    def _is_empty(self, opt, value=None):
         "convenience method to know if an option is empty"
+        if value is not None:
+            return False
         if (not opt.is_multi() and self._get_value(opt) == None) or \
             (opt.is_multi() and (self._get_value(opt) == [] or \
                 None in self._get_value(opt))):
             return True
         return False
 
-    def _test_mandatory(self, opt):
+    def _test_mandatory(self, opt, value=None):
         # mandatory options
         mandatory = self.context._cfgimpl_settings.mandatory
         if opt.is_mandatory() and mandatory:
-            if self._is_empty(opt) and \
+            if self._is_empty(opt, value) and \
                     opt.is_empty_by_default():
                 raise MandatoryError("option: {0} is mandatory "
                                       "and shall have a value".format(opt._name))
 
-    def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
+    def fill_multi(self, opt, result):
         """fills a multi option with default and calculated values
         """
         value = self._get_value(opt)
@@ -71,6 +95,7 @@ class OptionValues(object):
 
     def __getitem__(self, opt):
         # options with callbacks
+        value = self._get_value(opt)
         if opt.has_callback():
             if (not opt.is_frozen() or \
                     not opt.is_forced_on_freeze()) and \
@@ -83,68 +108,57 @@ class OptionValues(object):
                 pass
             else:
                 if opt.is_multi():
-                    #FIXME revoir les multis
-                    _result = fill_multi(opt, result)
+                    value = fill_multi(opt, 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.validate(_result,
+                        raise ConfigError('invalid calculated value returned '
+                            'for option {0} : shall not be a list'.format(name))
+                    value = result
+                if value != None and not opt.validate(value,
                             self.context._cfgimpl_settings.validator):
                     raise ConfigError('invalid calculated value returned'
                         ' for option {0}'.format(name))
-                self.values[opt] = _result
-                self.owners[opt] = owners.default
         # frozen and force default
         if not opt.has_callback() and opt.is_forced_on_freeze():
             value = opt.getdefault()
             if opt.is_multi():
-                #FIXME le fill_multi
-                value = self.fill_multi(opt, value,
-                            use_default_multi=True,
-                            default_multi=opt.getdefault_multi())
-            self.values[opt] = value
-            self.owners[opt] = owners.default
-        self._test_mandatory(opt)
-        value = self._get_value(opt)
+                value = self.fill_multi(opt, value)
+        self._test_mandatory(opt, value)
         return value
 
     def __setitem__(self, opt, value):
-        if opt in self.values:
-            old_value = self.values[opt]
-        else:
-            old_value = None
-        if type(old_value) == Multi:
-            self.previous_values[opt] = list(value)
-        else:
-            self.previous_values[opt] = value
+        self.set_previous_value(opt)
         self.values[opt] = value
+        self.setowner(opt, self.context._cfgimpl_settings.getowner())
 
     def __contains__(self, opt):
         return opt in self.values
-
     #____________________________________________________________
     def setowner(self, opt, owner):
-        pass
+        if isinstance(owner, owners.Owner):
+            self.owners[opt] = owner
+        else:
+            raise OptionValueError("Bad owner: " + str(owner))
 
     def getowner(self, opt):
         return self.owners.get(opt, owners.default)
+
 # ____________________________________________________________
 # multi types
 class Multi(list):
     """multi options values container
     that support item notation for the values of multi options"""
-    def __init__(self, lst, context, opt):
+    def __init__(self, lst, context, opt, multitype=settings.multitypes.default):
         """
         :param lst: the Multi wraps a list value
-        :param context: the context has the settings and the values
+        :param context: the home config that has the settings and the values
         :param opt: the option object that have this Multi value
         """
         self.settings = context._cfgimpl_settings
         self.opt = opt
         self.values = context._cfgimpl_values
+        self.multitype = multitype
         super(Multi, self).__init__(lst)
 
     def __setitem__(self, key, value):