Merge branch 'master' into orm
authorEmmanuel Garette <egarette@cadoles.com>
Tue, 4 Feb 2014 20:48:20 +0000 (21:48 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Tue, 4 Feb 2014 20:54:30 +0000 (21:54 +0100)
Conflicts:
tiramisu/config.py
tiramisu/option.py

1  2 
test/test_config_api.py
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/value.py

diff --combined test/test_config_api.py
@@@ -4,7 -4,8 +4,8 @@@ from py.test import raise
  
  from tiramisu.config import Config
  from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \
-     BoolOption, FilenameOption, OptionDescription
+     BoolOption, FilenameOption, UnicodeOption, SymLinkOption, IPOption, \
+     PortOption, OptionDescription
  
  
  def make_description():
      return descr
  
  
 -def test_compare_configs():
 -    "config object comparison"
 -    descr = make_description()
 -    conf1 = Config(descr)
 -    conf2 = Config(descr)
 -    conf2.wantref = True
 -    assert conf1 != conf2
 -    assert hash(conf1) != hash(conf2)
 -    #assert conf1.getkey() != conf2.getkey()
 -    conf1.wantref = True
 -    assert conf1 == conf2
 -    assert hash(conf1) == hash(conf2)
 -    #assert conf1.getkey() == conf2.getkey()
 -    conf2.gc.dummy = True
 -    assert conf1 != conf2
 -    assert hash(conf1) != hash(conf2)
 -    #assert conf1.getkey() != conf2.getkey()
 -    conf1.gc.dummy = True
 -    assert conf1 == conf2
 -    assert hash(conf1) == hash(conf2)
 -    assert not conf1 == 'conf2'
 -    assert conf1 != 'conf2'
 +#FIXME
 +#def test_compare_configs():
 +#    "config object comparison"
 +#    descr = make_description()
 +#    conf1 = Config(descr)
 +#    conf2 = Config(descr)
 +#    conf2.wantref = True
 +#    assert conf1 != conf2
 +#    assert hash(conf1) != hash(conf2)
 +#    #assert conf1.getkey() != conf2.getkey()
 +#    conf1.wantref = True
 +#    assert conf1 == conf2
 +#    assert hash(conf1) == hash(conf2)
 +#    #assert conf1.getkey() == conf2.getkey()
 +#    conf2.gc.dummy = True
 +#    assert conf1 != conf2
 +#    assert hash(conf1) != hash(conf2)
 +#    #assert conf1.getkey() != conf2.getkey()
 +#    conf1.gc.dummy = True
 +#    assert conf1 == conf2
 +#    assert hash(conf1) == hash(conf2)
 +#    assert not conf1 == 'conf2'
 +#    assert conf1 != 'conf2'
  # ____________________________________________________________
  
  
@@@ -131,7 -131,8 +132,7 @@@ def test_find_in_config()
      assert conf.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
      conf.read_write()
      raises(AttributeError, "assert conf.find(byname='prop')")
 -    assert conf.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')]
 -    #assert conf.find_first(byname='prop') == conf.unwrap_from_path('gc.prop')
 +    assert conf.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.prop'), conf.unwrap_from_path('gc.gc2.prop')]
      # combinaison of filters
      assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
      assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_path('gc.dummy')
      assert conf.gc.find_first(byname='bool', byvalue=False) == conf.unwrap_from_path('gc.gc2.bool')
      raises(AttributeError, "assert conf.gc.find_first(byname='bool', byvalue=True)")
      raises(AttributeError, "conf.gc.find(byname='wantref').first()")
 -    assert conf.gc.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')]
 +    assert conf.gc.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.prop'), conf.unwrap_from_path('gc.gc2.prop')]
      conf.read_only()
      assert conf.gc.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
      # not OptionDescription
      raises(AttributeError, "conf.find_first(byname='gc')")
      raises(AttributeError, "conf.gc.find_first(byname='gc2')")
+     raises(ValueError, "conf.find(byname='bool', type_='unknown')")
  
  
  def test_find_multi():
@@@ -208,3 -210,18 +210,18 @@@ def test_iter_all_prop()
      config = Config(descr)
      config.read_only()
      assert list(config.iter_all()) == [('string2', 'string2')]
+ def test_invalid_option():
+     raises(TypeError, "ChoiceOption('a', '', [1, 2])")
+     raises(TypeError, "ChoiceOption('a', '', 1)")
+     raises(TypeError, "ChoiceOption('a', '', (1,), open_values='string')")
+     raises(ValueError, "ChoiceOption('a', '', (1,), 3)")
+     raises(ValueError, "FloatOption('a', '', 'string')")
+     raises(ValueError, "UnicodeOption('a', '', 1)")
+     raises(ValueError, "SymLinkOption('a', 'string')")
+     raises(ValueError, "IPOption('a', '', 1)")
+     raises(ValueError, "IPOption('a', '', 'string')")
+     raises(ValueError, "PortOption('a', '', 'string')")
+     raises(ValueError, "PortOption('a', '', '11:12:13', allow_range=True)")
+     raises(ValueError, "PortOption('a', '', 11111111111111111111)")
diff --combined tiramisu/config.py
@@@ -73,24 -73,21 +73,24 @@@ class SubConfig(object)
                                   force_properties=force_properties)
          return self, path[-1]
  
 -    def __hash__(self):
 -        return hash(self.cfgimpl_get_description().impl_getkey(self))
 -
 -    def __eq__(self, other):
 -        "Config's comparison"
 -        if not isinstance(other, Config):
 -            return False
 -        return self.cfgimpl_get_description().impl_getkey(self) == \
 -            other.cfgimpl_get_description().impl_getkey(other)
 -
 -    def __ne__(self, other):
 -        "Config's comparison"
 -        if not isinstance(other, Config):
 -            return True
 -        return not self == other
 +    #def __hash__(self):
 +    #FIXME
 +    #    return hash(self.cfgimpl_get_description().impl_getkey(self))
 +
 +    #def __eq__(self, other):
 +    #FIXME
 +    #    "Config's comparison"
 +    #    if not isinstance(other, Config):
 +    #        return False
 +    #    return self.cfgimpl_get_description().impl_getkey(self) == \
 +    #        other.cfgimpl_get_description().impl_getkey(other)
 +
 +    #def __ne__(self, other):
 +    #FIXME
 +    #    "Config's comparison"
 +    #    if not isinstance(other, Config):
 +    #        return True
 +    #    return not self == other
  
      # ______________________________________________________________________
      def __iter__(self):
@@@ -99,7 -96,7 +99,7 @@@
          for child in self.cfgimpl_get_description().impl_getchildren():
              if not isinstance(child, OptionDescription):
                  try:
 -                    yield child._name, getattr(self, child._name)
 +                    yield child.impl_getname(), getattr(self, child.impl_getname())
                  except GeneratorExit:
                      raise StopIteration
                  except PropertiesOptionError:
          iteration on Options and OptionDescriptions."""
          for child in self.cfgimpl_get_description().impl_getchildren():
              try:
 -                yield child._name, getattr(self, child._name)
 +                yield child.impl_getname(), getattr(self, child.impl_getname())
              except GeneratorExit:
                  raise StopIteration
              except PropertiesOptionError:
                      if group_type is None or (group_type is not None and
                                                child.impl_get_group_type()
                                                == group_type):
 -                        yield child._name, getattr(self, child._name)
 +                        yield child.impl_getname(), getattr(self, child.impl_getname())
                  except GeneratorExit:
                      raise StopIteration
                  except PropertiesOptionError:
              finds a list of options recursively in the config
  
              :param bytype: Option class (BoolOption, StrOption, ...)
 -            :param byname: filter by Option._name
 +            :param byname: filter by Option.impl_getname()
              :param byvalue: filter by the option's value
              :returns: list of matching Option objects
          """
              finds an option recursively in the config
  
              :param bytype: Option class (BoolOption, StrOption, ...)
 -            :param byname: filter by Option._name
 +            :param byname: filter by Option.impl_getname()
              :param byvalue: filter by the option's value
              :returns: list of matching Option objects
          """
          :param first: return only one option if True, a list otherwise
          :return: find list or an exception if nothing has been found
          """
 -        def _filter_by_name():
 -            if byname is None or path == byname or \
 -                    path.endswith('.' + byname):
 -                return True
 -            return False
 -
          def _filter_by_value():
              if byvalue is None:
                  return True
                                             # upon the access of the value
                  return False
  
 -        def _filter_by_type():
 -            if bytype is None:
 -                return True
 -            if isinstance(option, bytype):
 -                return True
 -            return False
 -
          if type_ not in ('option', 'path', 'value'):
              raise ValueError(_('unknown type_ type {0}'
                                 'for _find').format(type_))
          find_results = []
 -        opts, paths = self.cfgimpl_get_description()._cache_paths
 -        for index in range(0, len(paths)):
 -            option = opts[index]
 -            if isinstance(option, OptionDescription):
 -                continue
 -            path = paths[index]
 -            if _subpath is not None and not path.startswith(_subpath + '.'):
 -                continue
 -            if not _filter_by_name():
 -                continue
 +        # if value and/or check_properties are set, need all avalaible option
 +        # If first one has no good value or not good property check second one
 +        # and so on
 +        only_first = first is True and byvalue is None and check_properties is None
 +        options = self.cfgimpl_get_description().impl_get_options_paths(
 +            bytype, byname, _subpath, only_first)
 +        for path, option in options:
              if not _filter_by_value():
                  continue
 -            if not _filter_by_type():
 -                continue
              #remove option with propertyerror, ...
              if byvalue is None and check_properties:
                  try:
                                 "option"))
          if withoption is not None:
              mypath = self.cfgimpl_get_path()
 -            for path in self._cfgimpl_get_context()._find(bytype=Option,
 +            for path in self._cfgimpl_get_context()._find(bytype=None,
                                                            byname=withoption,
                                                            byvalue=withvalue,
                                                            first=False,
          #withoption can be set to None below !
          if withoption is None:
              for opt in self.cfgimpl_get_description().impl_getchildren():
 -                path = opt._name
 +                path = opt.impl_getname()
                  self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten)
          if _currpath == []:
              options = dict(pathsvalues)
  
      def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten):
          if isinstance(opt, OptionDescription):
-             try:
-                 pathsvalues += getattr(self, path).make_dict(flatten,
-                                                              _currpath +
-                                                              path.split('.'))
-             except PropertiesOptionError:
-                 pass  # this just a hidden or disabled option
+             pathsvalues += getattr(self, path).make_dict(flatten,
+                                                          _currpath +
+                                                          path.split('.'))
          else:
 -            value = self._getattr(opt._name)
 -            if flatten:
 -                name = opt._name
 -            else:
 -                name = '.'.join(_currpath + [opt._name])
 -            pathsvalues.append((name, value))
 +            try:
 +                value = self._getattr(opt.impl_getname())
 +                if flatten:
 +                    name = opt.impl_getname()
 +                else:
 +                    name = '.'.join(_currpath + [opt.impl_getname()])
 +                pathsvalues.append((name, value))
 +            except PropertiesOptionError:
 +                pass  # this just a hidden or disabled option
  
      def cfgimpl_get_path(self):
          descr = self.cfgimpl_get_description()
@@@ -461,11 -470,8 +458,11 @@@ class _CommonConfig(SubConfig)
      "abstract base class for the Config, GroupConfig and the MetaConfig"
      __slots__ = ('_impl_values', '_impl_settings', '_impl_meta', '_impl_test')
  
 -    def _impl_build_all_paths(self):
 -        self.cfgimpl_get_description().impl_build_cache()
 +    def _impl_build_all_caches(self):
 +        if not self.cfgimpl_get_description().impl_already_build_caches():
 +            self.cfgimpl_get_description().impl_build_cache_consistency()
 +            self.cfgimpl_get_description().impl_validate_options()
 +            self.cfgimpl_get_description().impl_build_cache_option()
  
      def read_only(self):
          "read only is a global config's setting, see `settings.py`"
  # ____________________________________________________________
  class Config(_CommonConfig):
      "main configuration management entry"
 -    __slots__ = ('__weakref__',)
 +    __slots__ = ('__weakref__', '_impl_test')
  
      def __init__(self, descr, session_id=None, persistent=False):
          """ Configuration option management master class
          self._impl_settings = Settings(self, settings)
          self._impl_values = Values(self, values)
          super(Config, self).__init__(descr, weakref.ref(self))
 -        self._impl_build_all_paths()
 +        self._impl_build_all_caches()
          self._impl_meta = None
          #undocumented option used only in test script
          self._impl_test = False
diff --combined tiramisu/option.py
@@@ -22,7 -22,7 +22,7 @@@
  # ____________________________________________________________
  import re
  import sys
 -from copy import copy, deepcopy
 +from copy import copy
  from types import FunctionType
  from IPy import IP
  import warnings
@@@ -32,9 -32,6 +32,9 @@@ from tiramisu.setting import groups, mu
  from tiramisu.i18n import _
  from tiramisu.autolib import carry_out_calculation
  
 +#FIXME : need storage...
 +from tiramisu.storage.sqlalchemy.option import StorageBase, StorageOptionDescription
 +
  name_regexp = re.compile(r'^\d+')
  forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
                     'make_dict', 'unwrap_from_path', 'read_only',
@@@ -56,42 -53,23 +56,42 @@@ def valid_name(name)
  #
  
  
 -class BaseOption(object):
 -    """This abstract base class stands for attribute access
 -    in options that have to be set only once, it is of course done in the
 -    __setattr__ method
 -    """
 -    __slots__ = ('_name', '_requires', '_properties', '_readonly',
 -                 '_calc_properties', '_impl_informations',
 -                 '_state_readonly', '_state_requires', '_stated')
 -
 -    def __init__(self, name, doc, requires, properties):
 +class Base(StorageBase):
 +    def __init__(self, name, doc, default=None, default_multi=None,
 +                 requires=None, multi=False, callback=None,
 +                 callback_params=None, validator=None, validator_params=None,
 +                 properties=None, warnings_only=False, choice_values=None,
 +                 choice_open_values=None):
          if not valid_name(name):
              raise ValueError(_("invalid name: {0} for option").format(name))
          self._name = name
 -        self._impl_informations = {}
          self.impl_set_information('doc', doc)
 -        self._calc_properties, self._requires = validate_requires_arg(
 -            requires, self._name)
 +        if requires is not None:
 +            self._calc_properties, self._requires = validate_requires_arg(
 +                requires, self._name)
 +        if not multi and default_multi is not None:
 +            raise ValueError(_("a default_multi is set whereas multi is False"
 +                             " in option: {0}").format(name))
 +        if default_multi is not None:
 +            try:
 +                self._validate(default_multi)
 +            except ValueError as err:
 +                raise ValueError(_("invalid default_multi value {0} "
 +                                   "for option {1}: {2}").format(
 +                                       str(default_multi), name, err))
 +        self._multi = multi
 +        if self._multi:
 +            if default is None:
 +                default = []
 +            self._multitype = multitypes.default
 +            self._default_multi = default_multi
 +        if callback is not None and ((not multi and (default is not None or
 +                                                     default_multi is not None))
 +                                     or (multi and (default != [] or
 +                                                    default_multi is not None))
 +                                     ):
 +            raise ValueError(_("default value not allowed if option: {0} "
 +                             "is calculated").format(name))
          if properties is None:
              properties = tuple()
          if not isinstance(properties, tuple):
                              ' must be a tuple').format(
                                  type(properties),
                                  self._name))
 -        if self._calc_properties is not None and properties is not tuple():
 -            set_forbidden_properties = set(properties) & self._calc_properties
 +        if validator is not None:
 +            validate_callback(validator, validator_params, 'validator')
 +            self._validator = validator
 +            if validator_params is not None:
 +                self._validator_params = validator_params
 +        if callback is None and callback_params is not None:
 +            raise ValueError(_("params defined for a callback function but "
 +                             "no callback defined"
 +                             " yet for option {0}").format(name))
 +        if callback is not None:
 +            validate_callback(callback, callback_params, 'callback')
 +            self._callback = callback
 +            if callback_params is not None:
 +                self._callback_params = callback_params
 +        if self._calc_properties != frozenset([]) and properties is not tuple():
 +            set_forbidden_properties = self._calc_properties & set(properties)
              if set_forbidden_properties != frozenset():
                  raise ValueError('conflict: properties already set in '
                                   'requirement {0}'.format(
                                       list(set_forbidden_properties)))
 -        self._properties = properties  # 'hidden', 'disabled'...
 +        if choice_values is not None:
 +            self._choice_values = choice_values
 +        if choice_open_values is not None:
 +            self._choice_open_values = choice_open_values
 +        self.impl_validate(default)
 +        if multi and default is None:
 +            self._default = []
 +        else:
 +            self._default = default
 +        self._properties = properties
 +        #for prop in properties:
 +            #self._properties.append(self._get_property_object(prop))
 +        self._warnings_only = warnings_only
 +        return super(Base, self).__init__()
  
 -    def __setattr__(self, name, value):
 -        """set once and only once some attributes in the option,
 -        like `_name`. `_name` cannot be changed one the option and
 -        pushed in the :class:`tiramisu.option.OptionDescription`.
  
 -        if the attribute `_readonly` is set to `True`, the option is
 -        "frozen" (which has noting to do with the high level "freeze"
 -        propertie or "read_only" property)
 -        """
 -        if not name.startswith('_state') and not name.startswith('_cache'):
 -            is_readonly = False
 -            # never change _name
 -            if name == '_name':
 -                try:
 -                    self._name
 -                    #so _name is already set
 -                    is_readonly = True
 -                except:
 -                    pass
 -            elif name != '_readonly':
 -                try:
 -                    if self._readonly is True:
 -                        is_readonly = True
 -                except AttributeError:
 -                    self._readonly = False
 -            if is_readonly:
 -                raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
 -                                       " read-only").format(
 -                                           self.__class__.__name__,
 -                                           self._name,
 -                                           name))
 -        object.__setattr__(self, name, value)
 +class BaseOption(Base):
 +    """This abstract base class stands for attribute access
 +    in options that have to be set only once, it is of course done in the
 +    __setattr__ method
 +    """
 +    #__slots__ = ('_name', '_requires', '_properties', '_readonly',
 +    #             '_calc_properties', '_impl_informations',
 +    #             '_state_readonly', '_state_requires', '_stated')
  
      # information
      def impl_set_information(self, key, value):
          :param key: information's key (ex: "help", "doc"
          :param value: information's value (ex: "the help string")
          """
 -        self._impl_informations[key] = value
 +        self._informations[key] = value
  
      def impl_get_information(self, key, default=None):
          """retrieves one information's item
  
          :param key: the item string (ex: "help")
          """
 -        if key in self._impl_informations:
 -            return self._impl_informations[key]
 +        if key in self._informations:
 +            return self._informations[key]
          elif default is not None:
              return default
          else:
              raise ValueError(_("information's item not found: {0}").format(
                  key))
  
 +    # ____________________________________________________________
 +    # serialize object
      def _impl_convert_requires(self, descr, load=False):
          """export of the requires during the serialization process
  
          for key, value in state.items():
              setattr(self, key, value)
  
 +    def impl_getname(self):
 +        return self._name
 +
  
 -class Option(BaseOption):
 +class OnlyOption(BaseOption):
 +    pass
 +
 +
 +class Option(OnlyOption):
      """
      Abstract base class for configuration option's.
  
      Reminder: an Option object is **not** a container for the value.
      """
 -    __slots__ = ('_multi', '_validator', '_default_multi', '_default',
 -                 '_state_callback', '_callback', '_multitype',
 -                 '_consistencies', '_warnings_only', '_master_slaves',
 -                 '_state_consistencies', '__weakref__')
 +#    __slots__ = ('_multi', '_validator', '_default_multi', '_default',
 +#                 '_state_callback', '_callback', '_multitype',
 +#                 '_consistencies', '_warnings_only', '_master_slaves',
 +#                 '_state_consistencies', '__weakref__')
      _empty = ''
  
      def __init__(self, name, doc, default=None, default_multi=None,
                   requires=None, multi=False, callback=None,
                   callback_params=None, validator=None, validator_params=None,
 -                 properties=None, warnings_only=False):
 +                 properties=None, warnings_only=False, choice_values=None,
 +                 choice_open_values=None):
          """
          :param name: the option's name
          :param doc: the option's description
                               Values()._warning contain message
  
          """
 -        super(Option, self).__init__(name, doc, requires, properties)
 -        self._multi = multi
 -        if validator is not None:
 -            validate_callback(validator, validator_params, 'validator')
 -            self._validator = (validator, validator_params)
 -        else:
 -            self._validator = None
 -        if not self._multi and default_multi is not None:
 -            raise ValueError(_("a default_multi is set whereas multi is False"
 -                             " in option: {0}").format(name))
 -        if default_multi is not None:
 -            try:
 -                self._validate(default_multi)
 -            except ValueError as err:
 -                raise ValueError(_("invalid default_multi value {0} "
 -                                   "for option {1}: {2}").format(
 -                                       str(default_multi), name, err))
 -        if callback is not None and (default is not None or
 -                                     default_multi is not None):
 -            raise ValueError(_("default value not allowed if option: {0} "
 -                             "is calculated").format(name))
 -        if callback is None and callback_params is not None:
 -            raise ValueError(_("params defined for a callback function but "
 -                             "no callback defined"
 -                             " yet for option {0}").format(name))
 -        if callback is not None:
 -            validate_callback(callback, callback_params, 'callback')
 -            self._callback = (callback, callback_params)
 -        else:
 -            self._callback = None
 -        if self._multi:
 -            if default is None:
 -                default = []
 -            self._multitype = multitypes.default
 -            self._default_multi = default_multi
 -        self._warnings_only = warnings_only
 -        self.impl_validate(default)
 -        self._default = default
 -        self._consistencies = None
 +        super(Option, self).__init__(name, doc, default, default_multi,
 +                                     requires, multi, callback,
 +                                     callback_params, validator,
 +                                     validator_params, properties,
 +                                     warnings_only, choice_values,
 +                                     choice_open_values)
 +
 +    def impl_is_readonly(self):
 +        try:
 +            if self._readonly is True:
 +                return True
 +        except AttributeError:
 +            pass
 +        return False
 +
 +    def __setattr__(self, name, value):
 +        """set once and only once some attributes in the option,
 +        like `_name`. `_name` cannot be changed one the option and
 +        pushed in the :class:`tiramisu.option.OptionDescription`.
 +
 +        if the attribute `_readonly` is set to `True`, the option is
 +        "frozen" (which has noting to do with the high level "freeze"
 +        propertie or "read_only" property)
 +        """
 +        #FIXME ne devrait pas pouvoir redefinir _option
 +        #FIXME c'est une merde pour sqlachemy surement un cache ...
 +        if not name == '_option' and not isinstance(value, tuple):
 +            is_readonly = False
 +            # never change _name
 +            if name == '_name':
 +                try:
 +                    if self._name is not None:
 +                        #so _name is already set
 +                        is_readonly = True
 +                #FIXME je n'aime pas ce except ...
 +                except KeyError:
 +                    pass
 +            elif name != '_readonly':
 +                is_readonly = self.impl_is_readonly()
 +            if is_readonly:
 +                raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
 +                                       " read-only").format(
 +                                           self.__class__.__name__,
 +                                           self._name,
 +                                           name))
 +        super(Option, self).__setattr__(name, value)
 +
 +    def impl_getrequires(self):
 +        return self._requires
  
      def _launch_consistency(self, func, option, value, context, index,
                              all_cons_opts):
  
          def val_validator(val):
              if self._validator is not None:
 -                if self._validator[1] is not None:
 -                    validator_params = deepcopy(self._validator[1])
 +                if self._validator_params is not None:
 +                    validator_params = {}
 +                    for val_param, values in self._validator_params.items():
 +                        validator_params[val_param] = values
 +                    #FIXME ... ca sert √† quoi ...
                      if '' in validator_params:
                          lst = list(validator_params[''])
                          lst.insert(0, val)
                      validator_params = {'': (val,)}
                  # Raise ValueError if not valid
                  carry_out_calculation(self, config=context,
 -                                      callback=self._validator[0],
 +                                      callback=self._validator,
                                        callback_params=validator_params)
  
          def do_validation(_value, _index=None):
                  self._validate(_value)
              except ValueError as err:
                  raise ValueError(_('invalid value for option {0}: {1}'
 -                                   '').format(self._name, err))
 +                                   '').format(self.impl_getname(), err))
              try:
                  # valid with self._validator
                  val_validator(_value)
                  self._second_level_validation(_value)
              except ValueError as err:
                  msg = _("invalid value for option {0}: {1}").format(
 -                    self._name, err)
 +                    self.impl_getname(), err)
                  if self._warnings_only:
                      warnings.warn_explicit(ValueWarning(msg, self),
                                             ValueWarning,
              do_validation(value, force_index)
          else:
              if not isinstance(value, list):
 -                raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self._name))
 +                raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self.impl_getname()))
              for index, val in enumerate(value):
                  do_validation(val, index)
  
          else:
              return True
  
 -    def impl_getkey(self, value):
 -        return value
 +    def impl_get_callback(self):
 +        return self._callback, self._callback_params
 +
 +    #def impl_getkey(self, value):
 +    #    return value
  
      def impl_is_multi(self):
          return self._multi
          :param other_opts: options used to validate value
          :type other_opts: `list` of `tiramisu.option.Option`
          """
 -        if self._consistencies is None:
 -            self._consistencies = []
 +        if self.impl_is_readonly():
 +            raise AttributeError(_("'{0}' ({1}) cannont add consistency, option is"
 +                                   " read-only").format(
 +                                       self.__class__.__name__,
 +                                       self._name))
          for opt in other_opts:
              if not isinstance(opt, Option):
                  raise ConfigError(_('consistency should be set with an option'))
              else:
                  self._launch_consistency(func, self, value, None,
                                           None, all_cons_opts)
 -        self._consistencies.append((func, all_cons_opts))
 +        self._add_consistency(func, all_cons_opts)
          self.impl_validate(self.impl_getdefault())
  
      def _cons_not_equal(self, opts, vals):
              for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
                  if val_inf == val_sup is not None:
                      raise ValueError(_("same value for {0} and {1}").format(
 -                        opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name))
 +                        opts[idx_inf].impl_getname(), opts[idx_inf + idx_sup + 1].impl_getname()))
  
      def _impl_convert_callbacks(self, descr, load=False):
          if not load and self._callback is None:
@@@ -682,7 -627,9 +682,7 @@@ class ChoiceOption(Option)
      The option can also have the value ``None``
      """
  
 -    __slots__ = ('_values', '_open_values')
 -    _opt_type = 'string'
 -
 +    #__slots__ = ('_values', '_open_values')
      def __init__(self, name, doc, values, default=None, default_multi=None,
                   requires=None, multi=False, callback=None,
                   callback_params=None, open_values=False, validator=None,
          """
          if not isinstance(values, tuple):
              raise TypeError(_('values must be a tuple for {0}').format(name))
 -        self._values = values
          if open_values not in (True, False):
              raise TypeError(_('open_values must be a boolean for '
                              '{0}').format(name))
 -        self._open_values = open_values
          super(ChoiceOption, self).__init__(name, doc, default=default,
                                             default_multi=default_multi,
                                             callback=callback,
                                             validator=validator,
                                             validator_params=validator_params,
                                             properties=properties,
 -                                           warnings_only=warnings_only)
 +                                           warnings_only=warnings_only,
 +                                           choice_values=values,
 +                                           choice_open_values=open_values)
  
      def impl_get_values(self):
 -        return self._values
 +        return self._choice_values
  
      def impl_is_openvalues(self):
 -        return self._open_values
 +        return self._choice_open_values
  
      def _validate(self, value):
-         if not self._choice_open_values and not value in self._choice_values:
+         if not self.impl_is_openvalues() and not value in self.impl_get_values():
              raise ValueError(_('value {0} is not permitted, '
                                 'only {1} is allowed'
 -                               '').format(value, self._values))
 +                               '').format(value, self._choice_values))
  
  
  class BoolOption(Option):
      "represents a choice between ``True`` and ``False``"
 -    __slots__ = tuple()
 -    _opt_type = 'bool'
 +#    __slots__ = tuple()
  
      def _validate(self, value):
          if not isinstance(value, bool):
  
  class IntOption(Option):
      "represents a choice of an integer"
 -    __slots__ = tuple()
 -    _opt_type = 'int'
 +#    __slots__ = tuple()
  
      def _validate(self, value):
          if not isinstance(value, int):
  
  class FloatOption(Option):
      "represents a choice of a floating point number"
 -    __slots__ = tuple()
 -    _opt_type = 'float'
 +    #__slots__ = tuple()
  
      def _validate(self, value):
          if not isinstance(value, float):
  
  class StrOption(Option):
      "represents the choice of a string"
 -    __slots__ = tuple()
 -    _opt_type = 'string'
 +    #__slots__ = tuple()
  
      def _validate(self, value):
          if not isinstance(value, str):
  if sys.version_info[0] >= 3:
      #UnicodeOption is same as StrOption in python 3+
      class UnicodeOption(StrOption):
 -        __slots__ = tuple()
 +        #__slots__ = tuple()
          pass
  else:
      class UnicodeOption(Option):
          "represents the choice of a unicode string"
 -        __slots__ = tuple()
 -        _opt_type = 'unicode'
 +        #__slots__ = tuple()
          _empty = u''
  
          def _validate(self, value):
                  raise ValueError(_('invalid unicode'))
  
  
 -class SymLinkOption(BaseOption):
 -    __slots__ = ('_name', '_opt', '_state_opt')
 -    _opt_type = 'symlink'
 +class SymLinkOption(OnlyOption):
 +    #__slots__ = ('_name', '_opt', '_state_opt', '_readonly', '_parent')
      #not return _opt consistencies
 -    _consistencies = None
 +    #_consistencies = None
  
      def __init__(self, name, opt):
          self._name = name
                                 'for symlink {0}').format(name))
          self._opt = opt
          self._readonly = True
 +        self._parent = None
 +        self.commit()
  
      def __getattr__(self, name):
 -        if name in ('_name', '_opt', '_opt_type', '_readonly'):
 +        if name in ('_opt', '_opt_type', '_readonly', 'impl_getname'):
              return object.__getattr__(self, name)
          else:
              return getattr(self._opt, name)
          del(self._state_opt)
          super(SymLinkOption, self)._impl_setstate(descr)
  
 +    def impl_get_information(self, key, default=None):
 +        return self._opt.impl_get_information(key, default)
 +
  
  class IPOption(Option):
      "represents the choice of an ip"
 -    __slots__ = ('_private_only', '_allow_reserved')
 -    _opt_type = 'ip'
 -
 +    #__slots__ = ('_private_only', '_allow_reserved')
      def __init__(self, name, doc, default=None, default_multi=None,
                   requires=None, multi=False, callback=None,
                   callback_params=None, validator=None, validator_params=None,
      def _validate(self, value):
          # sometimes an ip term starts with a zero
          # but this does not fit in some case, for example bind does not like it
-         for val in value.split('.'):
-             if val.startswith("0") and len(val) > 1:
-                 raise ValueError(_('invalid IP'))
+         try:
+             for val in value.split('.'):
+                 if val.startswith("0") and len(val) > 1:
+                     raise ValueError(_('invalid IP'))
+         except AttributeError:
+             #if integer for example
+             raise ValueError(_('invalid IP'))
          # 'standard' validation
          try:
              IP('{0}/32'.format(value))
@@@ -859,16 -813,18 +863,16 @@@ class PortOption(Option)
      Port number 0 is reserved and can't be used.
      see: http://en.wikipedia.org/wiki/Port_numbers
      """
 -    __slots__ = ('_allow_range', '_allow_zero', '_min_value', '_max_value')
 -    _opt_type = 'port'
 -
 +    #__slots__ = ('_allow_range', '_allow_zero', '_min_value', '_max_value')
      def __init__(self, name, doc, default=None, default_multi=None,
                   requires=None, multi=False, callback=None,
                   callback_params=None, validator=None, validator_params=None,
                   properties=None, allow_range=False, allow_zero=False,
                   allow_wellknown=True, allow_registred=True,
                   allow_private=False, warnings_only=False):
 -        self._allow_range = allow_range
 -        self._min_value = None
 -        self._max_value = None
 +        extra = {'_allow_range': allow_range,
 +                 '_min_value': None,
 +                 '_max_value': None}
          ports_min = [0, 1, 1024, 49152]
          ports_max = [0, 1023, 49151, 65535]
          is_finally = False
                                           allow_wellknown,
                                           allow_registred,
                                           allow_private]):
 -            if self._min_value is None:
 +            if extra['_min_value'] is None:
                  if allowed:
 -                    self._min_value = ports_min[index]
 +                    extra['_min_value'] = ports_min[index]
              elif not allowed:
                  is_finally = True
              elif allowed and is_finally:
                  raise ValueError(_('inconsistency in allowed range'))
              if allowed:
 -                self._max_value = ports_max[index]
 +                extra['_max_value'] = ports_max[index]
  
 -        if self._max_value is None:
 +        if extra['_max_value'] is None:
              raise ValueError(_('max value is empty'))
  
          super(PortOption, self).__init__(name, doc, default=default,
                                           validator_params=validator_params,
                                           properties=properties,
                                           warnings_only=warnings_only)
 +        self._extra = extra
  
      def _validate(self, value):
 -        if self._allow_range and ":" in str(value):
 +        if self._extra['_allow_range'] and ":" in str(value):
              value = str(value).split(':')
              if len(value) != 2:
-                 raise ValueError('invalid part, range must have two values '
-                                  'only')
+                 raise ValueError(_('invalid part, range must have two values '
+                                  'only'))
              if not value[0] < value[1]:
-                 raise ValueError('invalid port, first port in range must be'
-                                  ' smaller than the second one')
+                 raise ValueError(_('invalid port, first port in range must be'
+                                  ' smaller than the second one'))
          else:
              value = [value]
  
          for val in value:
-             if not self._extra['_min_value'] <= int(val) <= self._extra['_max_value']:
-                 raise ValueError('invalid port, must be an between {0} and {1}'
-                                  ''.format(self._extra['_min_value'], self._extra['_max_value']))
+             try:
 -                if not self._min_value <= int(val) <= self._max_value:
 -                    raise ValueError(_('invalid port, must be an between {0} '
 -                                     'and {1}').format(self._min_value,
 -                                                      self._max_value))
++                if not self._extra['_min_value'] <= int(val) <= self._extra['_max_value']:
++                    raise ValueError('invalid port, must be an between {0} '
++                                     'and {1}'.format(
++                                         self._extra['_min_value'],
++                                         self._extra['_max_value']))
+             except ValueError:
+                 raise ValueError(_('invalid port'))
  
  
  class NetworkOption(Option):
      "represents the choice of a network"
 -    __slots__ = tuple()
 -    _opt_type = 'network'
 -
 +    #__slots__ = tuple()
      def _validate(self, value):
          try:
              IP(value)
  
  class NetmaskOption(Option):
      "represents the choice of a netmask"
 -    __slots__ = tuple()
 -    _opt_type = 'netmask'
 +    #__slots__ = tuple()
  
      def _validate(self, value):
          try:
              else:
                  msg = _('invalid network {0} ({1}) with netmask {2}')
          if msg is not None:
 -            raise ValueError(msg.format(val_ipnetwork, opts[1]._name,
 +            raise ValueError(msg.format(val_ipnetwork, opts[1].impl_getname(),
                                          val_netmask))
  
  
  class BroadcastOption(Option):
 -    __slots__ = tuple()
 -    _opt_type = 'broadcast'
 +    #__slots__ = tuple()
  
      def _validate(self, value):
          try:
          if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast):
              raise ValueError(_('invalid broadcast {0} ({1}) with network {2} '
                                 '({3}) and netmask {4} ({5})').format(
 -                                   broadcast, opts[0]._name, network,
 -                                   opts[1]._name, netmask, opts[2]._name))
 +                                   broadcast, opts[0].impl_getname(), network,
 +                                   opts[1].impl_getname(), netmask, opts[2].impl_getname()))
  
  
  class DomainnameOption(Option):
      domainname:
      fqdn: with tld, not supported yet
      """
 -    __slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
 -    _opt_type = 'domainname'
 +    #__slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
  
      def __init__(self, name, doc, default=None, default_multi=None,
                   requires=None, multi=False, callback=None,
                   warnings_only=False, allow_without_dot=False):
          if type_ not in ['netbios', 'hostname', 'domainname']:
              raise ValueError(_('unknown type_ {0} for hostname').format(type_))
 -        self._type = type_
 +        self._dom_type = type_
          if allow_ip not in [True, False]:
              raise ValueError(_('allow_ip must be a boolean'))
          if allow_without_dot not in [True, False]:
          end = ''
          extrachar = ''
          extrachar_mandatory = ''
 -        if self._type == 'netbios':
 +        if self._dom_type == 'netbios':
              length = 14
 -        elif self._type == 'hostname':
 +        elif self._dom_type == 'hostname':
              length = 62
 -        elif self._type == 'domainname':
 +        elif self._dom_type == 'domainname':
              length = 62
              if allow_without_dot is False:
                  extrachar_mandatory = '\.'
                  return
              except ValueError:
                  pass
 -        if self._type == 'domainname' and not self._allow_without_dot and \
 +        if self._dom_type == 'domainname' and not self._allow_without_dot and \
                  '.' not in value:
              raise ValueError(_("invalid domainname, must have dot"))
              if len(value) > 255:
  
  
  class EmailOption(DomainnameOption):
 -    __slots__ = tuple()
 -    _opt_type = 'email'
 +    #__slots__ = tuple()
      username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
  
      def _validate(self, value):
  
  
  class URLOption(DomainnameOption):
 -    __slots__ = tuple()
 -    _opt_type = 'url'
 +    #__slots__ = tuple()
      proto_re = re.compile(r'(http|https)://')
      path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
  
  
  
  class FilenameOption(Option):
 -    __slots__ = tuple()
 -    _opt_type = 'file'
 +    #__slots__ = tuple()
      path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
  
      def _validate(self, value):
              raise ValueError(_('invalid filename'))
  
  
 -class OptionDescription(BaseOption):
 +class OptionDescription(BaseOption, StorageOptionDescription):
      """Config's schema (organisation, group) and container of Options
      The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
      """
 -    __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
 -                 '_state_group_type', '_properties', '_children',
 -                 '_cache_consistencies', '_calc_properties', '__weakref__',
 -                 '_readonly', '_impl_informations', '_state_requires',
 -                 '_stated', '_state_readonly')
 -    _opt_type = 'optiondescription'
 +    #_slots = ('_name', '_requires', '_cache_paths', '_group_type',
 +    #          '_state_group_type', '_properties', '_children',
 +    #          '_cache_consistencies', '_calc_properties', '__weakref__',
 +    #          '_readonly', '_impl_informations', '_state_requires',
 +    #          '_stated', '_state_readonly')
  
      def __init__(self, name, doc, children, requires=None, properties=None):
          """
          :param children: a list of options (including optiondescriptions)
  
          """
 -        super(OptionDescription, self).__init__(name, doc, requires, properties)
 -        child_names = [child._name for child in children]
 +        super(OptionDescription, self).__init__(name, doc=doc, requires=requires, properties=properties)
 +        child_names = [child.impl_getname() for child in children]
          #better performance like this
          valid_child = copy(child_names)
          valid_child.sort()
                  raise ConflictError(_('duplicate option name: '
                                        '{0}').format(child))
              old = child
 -        self._children = (tuple(child_names), tuple(children))
 -        self._cache_paths = None
 +        for child in children:
 +            if child._parent is not None:
 +                raise ConflictError(_('duplicate option: '
 +                                      '{0}').format(child))
 +            self._children.append(child)  # = (tuple(child_names), tuple(children))
 +        #FIXME pour dico !
 +        #self._cache_paths = None
          self._cache_consistencies = None
          # the group_type is useful for filtering OptionDescriptions in a config
          self._group_type = groups.default
  
 +    def impl_getrequires(self):
 +        return self._requires
 +
      def impl_getdoc(self):
          return self.impl_get_information('doc')
  
 -    def __getattr__(self, name):
 -        if name in self.__slots__:
 -            return object.__getattribute__(self, name)
 -        try:
 -            return self._children[1][self._children[0].index(name)]
 -        except ValueError:
 -            raise AttributeError(_('unknown Option {0} '
 -                                   'in OptionDescription {1}'
 -                                   '').format(name, self._name))
 -
 -    def impl_getkey(self, config):
 -        return tuple([child.impl_getkey(getattr(config, child._name))
 -                      for child in self.impl_getchildren()])
 +    def impl_validate(self, *args):
 +        """usefull for OptionDescription"""
 +        pass
  
      def impl_getpaths(self, include_groups=False, _currpath=None):
          """returns a list of all paths in self, recursively
              _currpath = []
          paths = []
          for option in self.impl_getchildren():
 -            attr = option._name
 +            attr = option.impl_getname()
              if isinstance(option, OptionDescription):
                  if include_groups:
                      paths.append('.'.join(_currpath + [attr]))
          return paths
  
      def impl_getchildren(self):
 -        return self._children[1]
 -
 -    def impl_build_cache(self,
 -                         cache_path=None,
 -                         cache_option=None,
 -                         _currpath=None,
 -                         _consistencies=None,
 -                         force_no_consistencies=False):
 -        if _currpath is None and self._cache_paths is not None:
 -            # cache already set
 -            return
 -        if _currpath is None:
 -            save = True
 -            _currpath = []
 -            if not force_no_consistencies:
 -                _consistencies = {}
 -        else:
 -            save = False
 -        if cache_path is None:
 -            cache_path = []
 +        #FIXME dans la base ??
 +        return self._children
 +        #for child in self._children:
 +        #    yield(session.query(child._type).filter_by(id=child.id).first())
 +
 +    def impl_build_cache_consistency(self, _consistencies=None, cache_option=None):
 +        #FIXME cache_option !
 +        if _consistencies is None:
 +            init = True
 +            _consistencies = {}
              cache_option = []
 +        else:
 +            init = False
          for option in self.impl_getchildren():
 -            attr = option._name
 -            if option in cache_option:
 -                raise ConflictError(_('duplicate option: {0}').format(option))
 -
 -            cache_option.append(option)
 -            if not force_no_consistencies:
 -                option._readonly = True
 -            cache_path.append(str('.'.join(_currpath + [attr])))
 +            cache_option.append(option.id)
              if not isinstance(option, OptionDescription):
 -                if not force_no_consistencies and \
 -                        option._consistencies is not None:
 -                    for consistency in option._consistencies:
 -                        func, all_cons_opts = consistency
 -                        for opt in all_cons_opts:
 -                            _consistencies.setdefault(opt,
 -                                                      []).append((func,
 -                                                                  all_cons_opts))
 +                for consistency in option._consistencies:
 +                    func = consistency.func
 +                    all_cons_opts = consistency.options
 +                    for opt in all_cons_opts:
 +                        _consistencies.setdefault(opt,
 +                                                  []).append((func,
 +                                                             all_cons_opts))
              else:
 -                _currpath.append(attr)
 -                option.impl_build_cache(cache_path,
 -                                        cache_option,
 -                                        _currpath,
 -                                        _consistencies,
 -                                        force_no_consistencies)
 -                _currpath.pop()
 -        if save:
 -            self._cache_paths = (tuple(cache_option), tuple(cache_path))
 -            if not force_no_consistencies:
 -                if _consistencies != {}:
 -                    self._cache_consistencies = {}
 -                    for opt, cons in _consistencies.items():
 -                        if opt not in cache_option:
 -                            raise ConfigError(_('consistency with option {0} which is not in Config').format(opt._name))
 -                        self._cache_consistencies[opt] = tuple(cons)
 -                self._readonly = True
 -
 -    def impl_get_opt_by_path(self, path):
 -        try:
 -            return self._cache_paths[0][self._cache_paths[1].index(path)]
 -        except ValueError:
 -            raise AttributeError(_('no option for path {0}').format(path))
 -
 -    def impl_get_path_by_opt(self, opt):
 -        try:
 -            return self._cache_paths[1][self._cache_paths[0].index(opt)]
 -        except ValueError:
 -            raise AttributeError(_('no option {0} found').format(opt))
 +                option.impl_build_cache_consistency(_consistencies, cache_option)
 +        if init and _consistencies != {}:
 +            self._cache_consistencies = {}
 +            for opt, cons in _consistencies.items():
 +                #FIXME dans le cache ...
 +                if opt.id not in cache_option:
 +                    raise ConfigError(_('consistency with option {0} '
 +                                        'which is not in Config').format(
 +                                            opt.impl_getname()))
 +                self._cache_consistencies[opt] = tuple(cons)
 +
 +    def impl_validate_options(self, cache_option=None):
 +        """validate duplicate option and set option has readonly option
 +        """
 +        if cache_option is None:
 +            init = True
 +            cache_option = []
 +        else:
 +            init = False
 +        for option in self.impl_getchildren():
 +            #FIXME specifique id for sqlalchemy?
 +            #FIXME avec sqlalchemy ca marche le multi parent ? (dans des configs diff√©rentes)
 +            if option.id is None:
 +                raise SystemError(_("an option's id should not be None "
 +                                    "for {0}").format(option.impl_getname()))
 +            if option.id in cache_option:
 +                raise ConflictError(_('duplicate option: {0}').format(option))
 +            cache_option.append(option.id)
 +            option._readonly = True
 +            if isinstance(option, OptionDescription):
 +                option.impl_validate_options(cache_option)
 +        if init:
 +            self._readonly = True
  
      # ____________________________________________________________
      def impl_set_group_type(self, group_type):
                  for child in self.impl_getchildren():
                      if isinstance(child, OptionDescription):
                          raise ValueError(_("master group {0} shall not have "
 -                                         "a subgroup").format(self._name))
 +                                         "a subgroup").format(self.impl_getname()))
                      if isinstance(child, SymLinkOption):
                          raise ValueError(_("master group {0} shall not have "
 -                                         "a symlinkoption").format(self._name))
 +                                         "a symlinkoption").format(self.impl_getname()))
                      if not child.impl_is_multi():
                          raise ValueError(_("not allowed option {0} "
                                           "in group {1}"
                                           ": this option is not a multi"
 -                                         "").format(child._name, self._name))
 -                    if child._name == self._name:
 +                                         "").format(child.impl_getname(), self.impl_getname()))
 +                    if child._name == self.impl_getname():
                          child._multitype = multitypes.master
                          master = child
                      else:
                  if master is None:
                      raise ValueError(_('master group with wrong'
                                         ' master name for {0}'
 -                                       ).format(self._name))
 -                if master._callback is not None and master._callback[1] is not None:
 -                    for key, callbacks in master._callback[1].items():
 +                                       ).format(self.impl_getname()))
 +                master_callback, master_callback_params = master.impl_get_callback()
 +                if master_callback is not None and master_callback_params is not None:
 +                    for key, callbacks in master_callback_params.items():
                          for callbk in callbacks:
                              if isinstance(callbk, tuple):
                                  if callbk[0] in slaves:
              raise ValueError(_('group_type: {0}'
                                 ' not allowed').format(group_type))
  
 -    def impl_get_group_type(self):
 -        return self._group_type
 -
      def _valid_consistency(self, option, value, context, index):
          if self._cache_consistencies is None:
              return True
                      return False
          return True
  
 +    # ____________________________________________________________
 +    # serialize object
 +
      def _impl_getstate(self, descr=None):
          """enables us to export into a dict
          :param descr: parent :class:`tiramisu.option.OptionDescription`
diff --combined tiramisu/setting.py
@@@ -267,11 -267,11 +267,11 @@@ class Property(object)
          :param propname: a predefined or user defined property name
          :type propname: string
          """
 -        if self._opt is not None and self._opt._calc_properties is not None \
 +        if self._opt is not None and self._opt.impl_getrequires() is not None \
                  and propname in self._opt._calc_properties:
              raise ValueError(_('cannot append {0} property for option {1}: '
                                 'this property is calculated').format(
 -                                   propname, self._opt._name))
 +                                   propname, self._opt.impl_getname()))
          self._properties.add(propname)
          self._setting._setproperties(self._properties, self._opt, self._path)
  
@@@ -372,7 -372,7 +372,7 @@@ class Settings(object)
  
      def _getproperties(self, opt=None, path=None, is_apply_req=True):
          if opt is None:
-             props = self._p_.getproperties(path, default_properties)
+             props = copy(self._p_.getproperties(path, default_properties))
          else:
              if path is None:
                  raise ValueError(_('if opt is not None, path should not be'
                      ntime = int(time())
                  is_cached, props = self._p_.getcache(path, ntime)
                  if is_cached:
-                     return props
-             props = self._p_.getproperties(path, opt._properties)
+                     return copy(props)
+             props = copy(self._p_.getproperties(path, opt._properties))
              if is_apply_req:
                  props |= self.apply_requires(opt, path)
              if 'cache' in self:
          if opt is None:
              self._p_.setproperties(None, properties)
          else:
 -            if opt._calc_properties is not None:
 -                properties -= opt._calc_properties
 -            if set(opt._properties) == properties:
 -                self._p_.reset_properties(path)
 -            else:
 -                self._p_.setproperties(path, properties)
 +            #if opt._calc_properties is not None:
 +            #    properties -= opt._calc_properties
 +            #if set(opt._properties) == properties:
 +            #    self._p_.reset_properties(path)
 +            #else:
 +            #    self._p_.setproperties(path, properties)
 +            self._p_.setproperties(path, properties)
          self._getcontext().cfgimpl_reset_cache()
  
      #____________________________________________________________
                           (typically with the `frozen` property)
          """
          # opt properties
-         properties = copy(self._getproperties(opt_or_descr, path))
-         self_properties = copy(self._getproperties())
+         properties = self._getproperties(opt_or_descr, path)
+         self_properties = self._getproperties()
          # remove opt permissive
          # permissive affect option's permission with or without permissive
          # global property
                  raise PropertiesOptionError(_('cannot change the value for '
                                                'option {0} this option is'
                                                ' frozen').format(
 -                                                  opt_or_descr._name),
 +                                                  opt_or_descr.impl_getname()),
                                              props)
              else:
                  raise PropertiesOptionError(_("trying to access to an option "
                                                "named: {0} with properties {1}"
 -                                              "").format(opt_or_descr._name,
 +                                              "").format(opt_or_descr.impl_getname(),
                                                           str(props)), props)
  
      def setpermissive(self, permissive, opt=None, path=None):
diff --combined tiramisu/value.py
@@@ -66,6 -66,8 +66,8 @@@ class Values(object)
          meta = self._getcontext().cfgimpl_get_meta()
          if meta is not None:
              value = meta.cfgimpl_get_values()[opt]
+             if isinstance(value, Multi):
+                 value = list(value)
          else:
              value = opt.impl_getdefault()
          if opt.impl_is_multi():
          :type index: int
          :returns: a calculated value
          """
 -        callback, callback_params = opt._callback
 +        callback, callback_params = opt.impl_get_callback()
          if callback_params is None:
              callback_params = {}
          return carry_out_calculation(opt, config=self._getcontext(),
@@@ -459,7 -461,7 +461,7 @@@ class Multi(list)
          if valuelen > masterlen or (valuelen < masterlen and setitem):
              raise SlaveError(_("invalid len for the slave: {0}"
                                 " which has {1} as master").format(
 -                                   self.opt._name, masterp))
 +                                   self.opt.impl_getname(), masterp))
          elif valuelen < masterlen:
              for num in range(0, masterlen - valuelen):
                  if self.opt.impl_has_callback():
          if not force:
              if self.opt.impl_get_multitype() == multitypes.slave:
                  raise SlaveError(_("cannot append a value on a multi option {0}"
 -                                   " which is a slave").format(self.opt._name))
 +                                   " which is a slave").format(self.opt.impl_getname()))
              elif self.opt.impl_get_multitype() == multitypes.master:
                  values = context.cfgimpl_get_values()
                  if value is undefined and self.opt.impl_has_callback():
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot sort multi option {0} if master or slave"
 -                               "").format(self.opt._name))
 +                               "").format(self.opt.impl_getname()))
          if sys.version_info[0] >= 3:
              if cmp is not None:
                  raise ValueError(_('cmp is not permitted in python v3 or greater'))
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot reverse multi option {0} if master or "
 -                               "slave").format(self.opt._name))
 +                               "slave").format(self.opt.impl_getname()))
          super(Multi, self).reverse()
          self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
  
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot insert multi option {0} if master or "
 -                               "slave").format(self.opt._name))
 +                               "slave").format(self.opt.impl_getname()))
          super(Multi, self).insert(index, obj)
          self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
  
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot extend multi option {0} if master or "
 -                               "slave").format(self.opt._name))
 +                               "slave").format(self.opt.impl_getname()))
          super(Multi, self).extend(iterable)
          self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
  
                  raise ValueError(_("invalid value {0} "
                                     "for option {1}: {2}"
                                     "").format(str(value),
 -                                              self.opt._name, err))
 +                                              self.opt.impl_getname(), err))
  
      def pop(self, index, force=False):
          """the list value can be updated (poped)
          if not force:
              if self.opt.impl_get_multitype() == multitypes.slave:
                  raise SlaveError(_("cannot pop a value on a multi option {0}"
 -                                   " which is a slave").format(self.opt._name))
 -            if self.opt.impl_get_multitype() == multitypes.master:
 +                                   " which is a slave").format(self.opt.impl_getname()))
 +            elif self.opt.impl_get_multitype() == multitypes.master:
                  for slave in self.opt.impl_get_master_slaves():
                      values = context.cfgimpl_get_values()
                      if not values.is_default_owner(slave):