separate baseoption and option master
authorEmmanuel Garette <egarette@cadoles.com>
Mon, 24 Jul 2017 18:39:01 +0000 (20:39 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Mon, 24 Jul 2017 18:39:01 +0000 (20:39 +0200)
20 files changed:
tiramisu/config.py
tiramisu/option/__init__.py
tiramisu/option/baseoption.py
tiramisu/option/booloption.py
tiramisu/option/broadcastoption.py
tiramisu/option/choiceoption.py
tiramisu/option/dateoption.py
tiramisu/option/domainnameoption.py
tiramisu/option/floatoption.py
tiramisu/option/intoption.py
tiramisu/option/ipoption.py
tiramisu/option/netmaskoption.py
tiramisu/option/networkoption.py
tiramisu/option/option.py
tiramisu/option/optiondescription.py
tiramisu/option/passwordoption.py
tiramisu/option/portoption.py
tiramisu/option/stroption.py
tiramisu/option/urloption.py
tiramisu/value.py

index 6ab6474..0625d46 100644 (file)
@@ -307,7 +307,7 @@ class SubConfig(object):
                                                            context=context)
         if isinstance(child, OptionDescription) or isinstance(child, SynDynOptionDescription):
             raise TypeError(_("can't assign to an OptionDescription"))  # pragma: optional cover
-        elif isinstance(child, SymLinkOption) and \
+        elif child._is_symlinkoption() and \
                 not isinstance(child, DynSymLinkOption):  # pragma: no dynoptiondescription cover
             path = context.cfgimpl_get_description().impl_get_path_by_opt(
                 child._impl_getopt())
@@ -382,7 +382,7 @@ class SubConfig(object):
                     setting_properties=_setting_properties,
                     self_properties=_self_properties,
                     index=index)
-            elif isinstance(option, SymLinkOption):  # pragma: no dynoptiondescription cover
+            elif option._is_symlinkoption():  # pragma: no dynoptiondescription cover
                 path = context.cfgimpl_get_description().impl_get_path_by_opt(
                     option._impl_getopt())
                 cfg = context.getattr(path, validate=validate,
index 2437d3d..e4ad0fc 100644 (file)
@@ -1,7 +1,8 @@
 from .masterslave import MasterSlaves
 from .optiondescription import OptionDescription, DynOptionDescription, \
     SynDynOptionDescription
-from .baseoption import Option, SymLinkOption, DynSymLinkOption, submulti
+from .baseoption import SymLinkOption, DynSymLinkOption, submulti
+from .option import Option
 from .choiceoption import ChoiceOption
 from .booloption import BoolOption
 from .intoption import IntOption
index 4db060d..912ba52 100644 (file)
 # ____________________________________________________________
 import re
 from types import FunctionType
-import warnings
 import sys
 
 from ..i18n import _
-from ..setting import log, undefined, debug
-from ..autolib import carry_out_calculation
-from ..error import (ConfigError, ValueWarning, PropertiesOptionError,
-                     display_list)
+from ..setting import undefined
+from ..error import ConfigError
 
 if sys.version_info[0] >= 3:  # pragma: no cover
     from inspect import signature
@@ -36,16 +33,12 @@ else:
 
 STATIC_TUPLE = tuple()
 
-if sys.version_info[0] >= 3:  # pragma: no cover
-    xrange = range
-
 
 submulti = 2
 NAME_REGEXP = re.compile(r'^[a-z][a-zA-Z\d_]*$')
 FORBIDDEN_NAMES = frozenset(['iter_all', 'iter_group', 'find', 'find_first',
                              'make_dict', 'unwrap_from_path', 'read_only',
                              'read_write', 'getowner', 'set_contexts'])
-ALLOWED_CONST_LIST = ['_cons_not_equal']
 
 
 def valid_name(name):
@@ -64,10 +57,11 @@ def validate_callback(callback, callback_params, type_, callbackoption):
     """
     def _validate_option(option):
         #validate option
-        if isinstance(option, SymLinkOption):
-            cur_opt = option._impl_getopt()
-        elif isinstance(option, Option):
-            cur_opt = option
+        if hasattr(option, '_is_symlinkoption'):
+            if option._is_symlinkoption():
+                cur_opt = option._impl_getopt()
+            else:
+                cur_opt = option
         else:
             raise ValueError(_('{}_params must have an option'
                                ' not a {} for first argument'
@@ -205,7 +199,7 @@ class Base(object):
         return validator_params
 
     def _set_has_dependency(self):
-        if not isinstance(self, SymLinkOption):
+        if not self._is_symlinkoption():
             self._has_dependency = True
 
     def impl_has_dependency(self):
@@ -399,631 +393,12 @@ class BaseOption(Base):
                                     opt=self,
                                     path=path)
 
-
-class OnlyOption(BaseOption):
-    __slots__ = tuple()
-
-
-class Option(OnlyOption):
-    """
-    Abstract base class for configuration option's.
-
-    Reminder: an Option object is **not** a container for the value.
-    """
-    __slots__ = ('_extra',
-                 '_warnings_only',
-                 '_allow_empty_list',
-                 #multi
-                 '_multi',
-                 '_unique',
-                 #value
-                 '_default',
-                 '_default_multi',
-                 #calcul
-                 '_val_call',
-                 #
-                 '_master_slaves',
-                 '_choice_values',
-                 '_choice_values_params',
-                )
-    _empty = ''
-    def __init__(self, name, doc, default=None, default_multi=None,
-                 requires=None, multi=False, unique=undefined, callback=None,
-                 callback_params=None, validator=None, validator_params=None,
-                 properties=None, warnings_only=False, extra=None,
-                 allow_empty_list=undefined):
-
-        _setattr = object.__setattr__
-        if not multi and default_multi is not None:
-            raise ValueError(_("default_multi is set whereas multi is False"
-                               " in option: {0}").format(name))
-        if multi is True:
-            is_multi = True
-            _multi = 0
-        elif multi is False:
-            is_multi = False
-            _multi = 1
-        elif multi is submulti:
-            is_multi = True
-            _multi = submulti
-        else:
-            raise ValueError(_('invalid multi value'))
-        if _multi != 1:
-            _setattr(self, '_multi', _multi)
-        if multi is not False and default is None:
-            default = []
-        if validator is not None:
-            if multi:  # and validator_params is None:
-                validator_params = self._build_validator_params(validator, validator_params)
-
-            validate_callback(validator, validator_params, 'validator', self)
-            if validator_params is None:
-                val_call = (validator,)
-            else:
-                val_call = (validator, validator_params)
-            self._val_call = (val_call, None)
-            self._set_has_dependency()
-        if extra is not None:
-            _setattr(self, '_extra', extra)
-        if unique != undefined and not isinstance(unique, bool):
-            raise ValueError(_('unique must be a boolean'))
-        if not is_multi and unique is True:
-            raise ValueError(_('unique must be set only with multi value'))
-        if warnings_only is True:
-            _setattr(self, '_warnings_only', warnings_only)
-        if allow_empty_list is not undefined:
-            _setattr(self, '_allow_empty_list', allow_empty_list)
-
-        super(Option, self).__init__(name, doc, requires=requires,
-                                     properties=properties, is_multi=is_multi)
-        if is_multi and default_multi is not None:
-            err = self._validate(default_multi)
-            if err:
-                raise ValueError(_("invalid default_multi value {0} "
-                                   "for option {1}: {2}").format(
-                                       str(default_multi),
-                                       self.impl_getname(), str(err)))
-            _setattr(self, '_default_multi', default_multi)
-        if unique is not undefined:
-            _setattr(self, '_unique', unique)
-        err = self.impl_validate(default, is_multi=is_multi)
-        if err:
-            raise err
-        if (is_multi and default != []) or \
-                (not is_multi and default is not None):
-            if is_multi:
-                default = tuple(default)
-            _setattr(self, '_default', default)
-
-        self.impl_set_callback(callback, callback_params, _init=True)
-
-    def impl_is_multi(self):
-        return getattr(self, '_multi', 1) != 1
-
-    def _add_dependencies(self, option):
-        options = set(getattr(self, '_dependencies', tuple()))
-        options.add(option)
-        self._dependencies = tuple(options)
-
-    def _launch_consistency(self, current_opt, func, option, value, context,
-                            index, submulti_index, opts, warnings_only,
-                            transitive):
-        """Launch consistency now
-
-        :param func: function name, this name should start with _cons_
-        :type func: `str`
-        :param option: option that value is changing
-        :type option: `tiramisu.option.Option`
-        :param value: new value of this option
-        :param context: Config's context, if None, check default value instead
-        :type context: `tiramisu.config.Config`
-        :param index: only for multi option, consistency should be launch for
-                      specified index
-        :type index: `int`
-        :param opts: all options concerne by this consistency
-        :type opts: `list` of `tiramisu.option.Option`
-        :param warnings_only: specific raise error for warning
-        :type warnings_only: `boolean`
-        :param transitive: propertyerror is transitive
-        :type transitive: `boolean`
-        """
-        if context is not undefined:
-            descr = context.cfgimpl_get_description()
-
-        all_cons_vals = []
-        all_cons_opts = []
-        val_consistencies = True
-        for opt in opts:
-            if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \
-                    option == opt:
-                # option is current option
-                # we have already value, so use it
-                all_cons_vals.append(value)
-                all_cons_opts.append(opt)
-            else:
-                #if context, calculate value, otherwise get default value
-                path = None
-                is_multi = opt.impl_is_multi() and not opt.impl_is_master_slaves()
-                if context is not undefined:
-                    if isinstance(opt, DynSymLinkOption):
-                        path = opt.impl_getpath(context)
-                    else:
-                        path = descr.impl_get_path_by_opt(opt)
-                    if is_multi:
-                        _index = None
-                    else:
-                        _index = index
-                    opt_value = context.getattr(path, validate=False,
-                                                index=_index,
-                                                force_permissive=True,
-                                                returns_raise=True)
-                    if isinstance(opt_value, Exception):
-                        if isinstance(opt_value, PropertiesOptionError):
-                            if debug:  # pragma: no cover
-                                log.debug('propertyerror in _launch_consistency: {0}'.format(opt_value))
-                            if transitive:
-                                opt_value.set_orig_opt(option)
-                                return opt_value
-                            else:
-                                opt_value = None
-                        else:  # pragma: no cover
-                            return opt_value
-                elif index is None:
-                    opt_value = opt.impl_getdefault()
-                else:
-                    opt_value = opt.impl_getdefault()[index]
-
-                if self.impl_is_multi() and index is None:
-                    # only check propertyerror for master/slaves is transitive
-                    val_consistencies = False
-                if is_multi and isinstance(opt_value, list):
-                    all_cons_vals.extend(opt_value)
-                    for len_ in xrange(len(opt_value)):
-                        all_cons_opts.append(opt)
-                else:
-                    all_cons_vals.append(opt_value)
-                    all_cons_opts.append(opt)
-
-        if val_consistencies:
-            err = getattr(self, func)(current_opt, all_cons_opts, all_cons_vals, warnings_only)
-            if err:
-                if warnings_only:
-                    msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}').format(
-                        value, self._display_name, current_opt.impl_get_display_name(), err)
-                    warnings.warn_explicit(ValueWarning(msg, self),
-                                           ValueWarning,
-                                           self.__class__.__name__, 0)
-                else:
-                    return err
-
-    def impl_is_unique(self):
-        return getattr(self, '_unique', False)
-
-    def impl_get_validator(self):
-        val = getattr(self, '_val_call', (None,))[0]
-        if val is None:
-            ret_val = (None, {})
-        elif len(val) == 1:
-            ret_val = (val[0], {})
-        else:
-            ret_val = val
-        return ret_val
-
-    def impl_validate(self, value, context=undefined, validate=True,
-                      force_index=None, force_submulti_index=None,
-                      current_opt=undefined, is_multi=None,
-                      display_error=True, display_warnings=True, multi=None,
-                      setting_properties=undefined):
-        """
-        :param value: the option's value
-        :param context: Config's context
-        :type context: :class:`tiramisu.config.Config`
-        :param validate: if true enables ``self._validator`` validation
-        :type validate: boolean
-        :param force_index: if multi, value has to be a list
-                            not if force_index is not None
-        :type force_index: integer
-        :param force_submulti_index: if submulti, value has to be a list
-                                     not if force_submulti_index is not None
-        :type force_submulti_index: integer
-        """
-        if not validate:
-            return
-        if current_opt is undefined:
-            current_opt = self
-
-        if display_warnings and setting_properties is undefined and context is not undefined:
-            setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=False)
-        display_warnings = display_warnings and (setting_properties is undefined or 'warnings' in setting_properties)
-
-        def _is_not_unique(value):
-            if display_error and self.impl_is_unique() and len(set(value)) != len(value):
-                for idx, val in enumerate(value):
-                    if val in value[idx+1:]:
-                        return ValueError(_('invalid value "{}", this value is already in "{}"').format(
-                                            val, self.impl_get_display_name()))
-
-        def calculation_validator(val, _index):
-            validator, validator_params = self.impl_get_validator()
-            if validator is not None:
-                if validator_params != {}:
-                    validator_params_ = {}
-                    for val_param, values in validator_params.items():
-                        validator_params_[val_param] = values
-                    #inject value in calculation
-                    if '' in validator_params_:
-                        lst = list(validator_params_[''])
-                        lst.insert(0, val)
-                        validator_params_[''] = tuple(lst)
-                    else:
-                        validator_params_[''] = (val,)
-                else:
-                    validator_params_ = {'': (val,)}
-                # Raise ValueError if not valid
-                value = carry_out_calculation(current_opt, context=context,
-                                              callback=validator,
-                                              callback_params=validator_params_,
-                                              index=_index,
-                                              is_validator=True)
-                if isinstance(value, Exception):
-                    return value
-
-        def do_validation(_value, _index, submulti_index):
-            if _value is None:
-                error = warning = None
-            else:
-                if display_error:
-                    # option validation
-                    err = self._validate(_value, context, current_opt)
-                    if err:
-                        if debug:  # pragma: no cover
-                            log.debug('do_validation: value: {0}, index: {1}, '
-                                      'submulti_index: {2}'.format(_value, _index,
-                                                                   submulti_index),
-                                      exc_info=True)
-                        err_msg = '{0}'.format(err)
-                        if err_msg:
-                            msg = _('"{0}" is an invalid {1} for "{2}", {3}'
-                                    '').format(_value, self._display_name,
-                                               self.impl_get_display_name(), err_msg)
-                        else:
-                            msg = _('"{0}" is an invalid {1} for "{2}"'
-                                    '').format(_value, self._display_name,
-                                               self.impl_get_display_name())
-                        return ValueError(msg)
-                error = None
-                is_warnings_only = getattr(self, '_warnings_only', False)
-                if ((display_error and not is_warnings_only) or
-                        (display_warnings and is_warnings_only)):
-                    error = calculation_validator(_value, _index)
-                    if not error:
-                        error = self._second_level_validation(_value, is_warnings_only)
-                    if error:
-                        if debug:  # pragma: no cover
-                            log.debug(_('do_validation for {0}: error in value').format(
-                                self.impl_getname()), exc_info=True)
-                        if is_warnings_only:
-                            msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}').format(
-                                _value, self._display_name, self.impl_get_display_name(), error)
-                            warnings.warn_explicit(ValueWarning(msg, self),
-                                                   ValueWarning,
-                                                   self.__class__.__name__, 0)
-                            error = None
-            if error is None:
-                # if context launch consistency validation
-                #if context is not undefined:
-                ret = self._valid_consistency(current_opt, _value, context,
-                                              _index, submulti_index, display_warnings,
-                                              display_error)
-                if isinstance(ret, ValueError):
-                    error = ret
-                elif ret:
-                    return ret
-            if error:
-                err_msg = '{0}'.format(error)
-                if err_msg:
-                    msg = _('"{0}" is an invalid {1} for "{2}", {3}'
-                            '').format(_value, self._display_name,
-                                       self.impl_get_display_name(), err_msg)
-                else:
-                    msg = _('"{0}" is an invalid {1} for "{2}"'
-                            '').format(_value, self._display_name,
-                                       self.impl_get_display_name())
-                return ValueError(msg)
-
-        if is_multi is None:
-            is_multi = self.impl_is_multi()
-
-        if not is_multi:
-            return do_validation(value, None, None)
-        elif force_index is not None:
-            if self.impl_is_submulti() and force_submulti_index is None:
-                err = _is_not_unique(value)
-                if err:
-                    return err
-                if not isinstance(value, list):
-                    return ValueError(_('invalid value "{0}" for "{1}" which'
-                                        ' must be a list').format(
-                                           value, self.impl_get_display_name()))
-                for idx, val in enumerate(value):
-                    if isinstance(val, list):  # pragma: no cover
-                        return ValueError(_('invalid value "{}" for "{}" '
-                                            'which must not be a list').format(val,
-                                                                              self.impl_get_display_name()))
-                    err = do_validation(val, force_index, idx)
-                    if err:
-                        return err
-            else:
-                if multi is not None and self.impl_is_unique() and value in multi:
-                    if not self.impl_is_submulti() and len(multi) - 1 >= force_index:
-                        lst = list(multi)
-                        lst.pop(force_index)
-                    else:
-                        lst = multi
-                    if value in lst:
-                        return ValueError(_('invalid value "{}", this value is already'
-                                            ' in "{}"').format(value,
-                                                               self.impl_get_display_name()))
-                return do_validation(value, force_index, force_submulti_index)
-        elif not isinstance(value, list):
-            return ValueError(_('invalid value "{0}" for "{1}" which '
-                                'must be a list').format(value,
-                                                         self.impl_getname()))
-        elif self.impl_is_submulti() and force_submulti_index is None:
-            for idx, val in enumerate(value):
-                err = _is_not_unique(val)
-                if err:
-                    return err
-                if not isinstance(val, list):
-                    return ValueError(_('invalid value "{0}" for "{1}" '
-                                        'which must be a list of list'
-                                        '').format(val,
-                                                   self.impl_getname()))
-                for slave_idx, slave_val in enumerate(val):
-                    err = do_validation(slave_val, idx, slave_idx)
-                    if err:
-                        return err
-        else:
-            err = _is_not_unique(value)
-            if err:
-                return err
-            for idx, val in enumerate(value):
-                err = do_validation(val, idx, force_submulti_index)
-                if err:
-                    return err
-            return self._valid_consistency(current_opt, None, context,
-                                           None, None, display_warnings, display_error)
-
-    def impl_is_dynsymlinkoption(self):
+    def _is_symlinkoption(self):
         return False
 
-    def impl_is_master_slaves(self, type_='both'):
-        """FIXME
-        """
-        master_slaves = self.impl_get_master_slaves()
-        if master_slaves is not None:
-            if type_ in ('both', 'master') and \
-                    master_slaves.is_master(self):
-                return True
-            if type_ in ('both', 'slave') and \
-                    not master_slaves.is_master(self):
-                return True
-        return False
-
-    def impl_get_master_slaves(self):
-        return getattr(self, '_master_slaves', None)
-
-    def impl_getdoc(self):
-        "accesses the Option's doc"
-        return self.impl_get_information('doc')
 
-    def _valid_consistencies(self, other_opts, init=True, func=None):
-        if self._is_subdyn():
-            dynod = self._subdyn
-        else:
-            dynod = None
-        if self.impl_is_submulti():
-            raise ConfigError(_('cannot add consistency with submulti option'))
-        is_multi = self.impl_is_multi()
-        for opt in other_opts:
-            if opt.impl_is_submulti():
-                raise ConfigError(_('cannot add consistency with submulti option'))
-            if not isinstance(opt, Option):
-                raise ConfigError(_('consistency must be set with an option'))
-            if opt._is_subdyn():
-                if dynod is None:
-                    raise ConfigError(_('almost one option in consistency is '
-                                        'in a dynoptiondescription but not all'))
-                if dynod != opt._subdyn:
-                    raise ConfigError(_('option in consistency must be in same'
-                                        ' dynoptiondescription'))
-                dynod = opt._subdyn
-            elif dynod is not None:
-                raise ConfigError(_('almost one option in consistency is in a '
-                                    'dynoptiondescription but not all'))
-            if self is opt:
-                raise ConfigError(_('cannot add consistency with itself'))
-            if is_multi != opt.impl_is_multi():
-                raise ConfigError(_('every options in consistency must be '
-                                    'multi or none'))
-            if init:
-                # FIXME
-                if func != 'not_equal':
-                    opt._set_has_dependency()
-
-    def impl_add_consistency(self, func, *other_opts, **params):
-        """Add consistency means that value will be validate with other_opts
-        option's values.
-
-        :param func: function's name
-        :type func: `str`
-        :param other_opts: options used to validate value
-        :type other_opts: `list` of `tiramisu.option.Option`
-        :param params: extra params (warnings_only and transitive are allowed)
-        """
-        if self.impl_is_readonly(): 
-            raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is"
-                                   " read-only").format(
-                                       self.__class__.__name__,
-                                       self.impl_getname()))
-        self._valid_consistencies(other_opts, func=func)
-        func = '_cons_{0}'.format(func)
-        if func not in dir(self):
-            raise ConfigError(_('consistency {0} not available for this option').format(func))
-        all_cons_opts = tuple([self] + list(other_opts))
-        unknown_params = set(params.keys()) - set(['warnings_only', 'transitive'])
-        if unknown_params != set():
-            raise ValueError(_('unknow parameter {0} in consistency').format(unknown_params))
-        self._add_consistency(func, all_cons_opts, params)
-        #validate default value when add consistency
-        err = self.impl_validate(self.impl_getdefault())
-        if err:
-            self._del_consistency()
-            raise err
-        if func in ALLOWED_CONST_LIST:
-            for opt in all_cons_opts:
-                if getattr(opt, '_unique', undefined) == undefined:
-                    opt._unique = True
-        if func != '_cons_not_equal':
-            #consistency could generate warnings or errors
-            self._set_has_dependency()
-
-    def _valid_consistency(self, option, value, context, index, submulti_idx,
-                           display_warnings, display_error):
-        if context is not undefined:
-            descr = context.cfgimpl_get_description()
-            if descr._cache_consistencies is None:
-                return
-            #consistencies is something like [('_cons_not_equal', (opt1, opt2))]
-            if isinstance(option, DynSymLinkOption):
-                consistencies = descr._cache_consistencies.get(option._impl_getopt())
-            else:
-                consistencies = descr._cache_consistencies.get(option)
-        else:
-            consistencies = option._get_consistencies()
-        if consistencies is not None:
-            for func, all_cons_opts, params in consistencies:
-                warnings_only = params.get('warnings_only', False)
-                if (warnings_only and display_warnings) or (not warnings_only and display_error):
-                    transitive = params.get('transitive', True)
-                    #all_cons_opts[0] is the option where func is set
-                    if isinstance(option, DynSymLinkOption):
-                        subpath = '.'.join(option._dyn.split('.')[:-1])
-                        namelen = len(option._impl_getopt().impl_getname())
-                        suffix = option.impl_getname()[namelen:]
-                        opts = []
-                        for opt in all_cons_opts:
-                            name = opt.impl_getname() + suffix
-                            path = subpath + '.' + name
-                            opts.append(opt._impl_to_dyn(name, path))
-                    else:
-                        opts = all_cons_opts
-                    err = opts[0]._launch_consistency(self, func, option, value,
-                                                      context, index, submulti_idx,
-                                                      opts, warnings_only,
-                                                      transitive)
-                    if err:
-                        return err
-
-    def _cons_not_equal(self, current_opt, opts, vals, warnings_only):
-        equal = set()
-        is_current = False
-        for idx_inf, val_inf in enumerate(vals):
-            for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
-                if val_inf == val_sup is not None:
-                    for opt_ in [opts[idx_inf], opts[idx_inf + idx_sup + 1]]:
-                        if opt_ == current_opt:
-                            is_current = True
-                        else:
-                            equal.add(opt_)
-        if equal:
-            if debug:  # pragma: no cover
-                log.debug(_('_cons_not_equal: {} are not different').format(display_list(list(equal))))
-            if is_current:
-                if warnings_only:
-                    msg = _('should be different from the value of {}')
-                else:
-                    msg = _('must be different from the value of {}')
-            else:
-                if warnings_only:
-                    msg = _('value for {} should be different')
-                else:
-                    msg = _('value for {} must be different')
-            equal_name = []
-            for opt in equal:
-                equal_name.append(opt.impl_get_display_name())
-            return ValueError(msg.format(display_list(list(equal_name))))
-
-    def _second_level_validation(self, value, warnings_only):
-        pass
-
-    def _impl_to_dyn(self, name, path):
-        return DynSymLinkOption(name, self, dyn=path)
-
-    def impl_getdefault_multi(self):
-        "accessing the default value for a multi"
-        return getattr(self, '_default_multi', None)
-
-    def _validate_callback(self, callback, callback_params):
-        """callback_params:
-        * None
-        * {'': ((option, permissive),), 'ip': ((None,), (option, permissive))
-        """
-        if callback is None:
-            return
-        default_multi = self.impl_getdefault_multi()
-        is_multi = self.impl_is_multi()
-        default = self.impl_getdefault()
-        if (not is_multi and (default is not None or default_multi is not None)) or \
-                (is_multi and (default != [] or default_multi is not None)):
-            raise ValueError(_("default value not allowed if option: {0} "
-                             "is calculated").format(self.impl_getname()))
-
-    def impl_getdefault(self):
-        "accessing the default value"
-        is_multi = self.impl_is_multi()
-        default = getattr(self, '_default', undefined)
-        if default is undefined:
-            if is_multi:
-                default = []
-            else:
-                default = None
-        else:
-            if is_multi:
-                default = list(default)
-        return default
-
-    def _get_extra(self, key):
-        extra = self._extra
-        if isinstance(extra, tuple):
-            return extra[1][extra[0].index(key)]
-        else:
-            return extra[key]
-
-    def impl_is_submulti(self):
-        return getattr(self, '_multi', 1) == 2
-
-    def impl_allow_empty_list(self):
-        return getattr(self, '_allow_empty_list', undefined)
-
-    #____________________________________________________________
-    # consistency
-    def _add_consistency(self, func, all_cons_opts, params):
-        cons = (func, all_cons_opts, params)
-        consistencies = getattr(self, '_consistencies', None)
-        if consistencies is None:
-            self._consistencies = [cons]
-        else:
-            consistencies.append(cons)
-
-    def _del_consistency(self):
-        self._consistencies.pop(-1)
-
-    def _get_consistencies(self):
-        return getattr(self, '_consistencies', STATIC_TUPLE)
-
-    def _has_consistencies(self):
-        return hasattr(self, '_consistencies')
+class OnlyOption(BaseOption):
+    __slots__ = tuple()
 
 
 def validate_requires_arg(new_option, multi, requires, name):
@@ -1045,7 +420,7 @@ def validate_requires_arg(new_option, multi, requires, name):
 
     def get_option(require):
         option = require['option']
-        if not isinstance(option, Option):
+        if not hasattr(option, '_is_symlinkoption'):
             raise ValueError(_('malformed requirements '
                                'must be an option in option {0}').format(name))
         if not multi and option.impl_is_multi():
@@ -1178,7 +553,8 @@ def validate_requires_arg(new_option, multi, requires, name):
 class SymLinkOption(OnlyOption):
 
     def __init__(self, name, opt):
-        if not isinstance(opt, Option):
+        if not isinstance(opt, OnlyOption) or \
+                opt._is_symlinkoption():
             raise ValueError(_('malformed symlinkoption '
                                'must be an option '
                                'for symlink {0}').format(name))
@@ -1187,6 +563,9 @@ class SymLinkOption(OnlyOption):
         _setattr(self, '_opt', opt)
         opt._set_has_dependency()
 
+    def _is_symlinkoption(self):
+        return True
+
     def __getattr__(self, name, context=undefined):
         return getattr(self._impl_getopt(), name)
 
index ed6581f..49f3a54 100644 (file)
@@ -21,7 +21,7 @@
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class BoolOption(Option):
index 175fd0e..c6170dd 100644 (file)
@@ -23,7 +23,7 @@ from IPy import IP
 from ..error import ConfigError
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class BroadcastOption(Option):
index eeffc53..32895c3 100644 (file)
@@ -22,9 +22,10 @@ from types import FunctionType
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option, validate_callback, display_list
+from .baseoption import validate_callback
+from .option import Option
 from ..autolib import carry_out_calculation
-from ..error import ConfigError
+from ..error import ConfigError, display_list
 
 
 class ChoiceOption(Option):
index 55c9304..b368827 100644 (file)
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
-import re
 from datetime import datetime
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class DateOption(Option):
index 765f5cb..dfe3552 100644 (file)
@@ -23,7 +23,7 @@ from IPy import IP
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class DomainnameOption(Option):
index 2eea4fa..9d2c6fc 100644 (file)
@@ -21,7 +21,7 @@
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class FloatOption(Option):
index 9ffcc51..fb47ef6 100644 (file)
@@ -21,7 +21,7 @@
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class IntOption(Option):
index 346eaec..c100494 100644 (file)
@@ -23,7 +23,7 @@ from IPy import IP
 from ..error import ConfigError
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class IPOption(Option):
index daa65f6..dcd9ef0 100644 (file)
@@ -23,7 +23,7 @@ from IPy import IP
 from ..error import ConfigError
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class NetmaskOption(Option):
index f891c4c..75f9e17 100644 (file)
@@ -22,7 +22,7 @@ from IPy import IP
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class NetworkOption(Option):
index 9545e49..2b550e2 100644 (file)
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
-from ..setting import undefined
+import warnings
+import sys
 
-from .baseoption import Option
+from .baseoption import OnlyOption, submulti, DynSymLinkOption, validate_callback, STATIC_TUPLE
+from ..i18n import _
+from ..setting import log, undefined, debug
+from ..autolib import carry_out_calculation
+from ..error import (ConfigError, ValueWarning, PropertiesOptionError,
+                     display_list)
+
+ALLOWED_CONST_LIST = ['_cons_not_equal']
+
+if sys.version_info[0] >= 3:  # pragma: no cover
+    xrange = range
+
+
+class Option(OnlyOption):
+    """
+    Abstract base class for configuration option's.
+
+    Reminder: an Option object is **not** a container for the value.
+    """
+    __slots__ = ('_extra',
+                 '_warnings_only',
+                 '_allow_empty_list',
+                 #multi
+                 '_multi',
+                 '_unique',
+                 #value
+                 '_default',
+                 '_default_multi',
+                 #calcul
+                 '_val_call',
+                 #
+                 '_master_slaves',
+                 '_choice_values',
+                 '_choice_values_params',
+                )
+    _empty = ''
+    def __init__(self, name, doc, default=None, default_multi=None,
+                 requires=None, multi=False, unique=undefined, callback=None,
+                 callback_params=None, validator=None, validator_params=None,
+                 properties=None, warnings_only=False, extra=None,
+                 allow_empty_list=undefined):
+
+        _setattr = object.__setattr__
+        if not multi and default_multi is not None:
+            raise ValueError(_("default_multi is set whereas multi is False"
+                               " in option: {0}").format(name))
+        if multi is True:
+            is_multi = True
+            _multi = 0
+        elif multi is False:
+            is_multi = False
+            _multi = 1
+        elif multi is submulti:
+            is_multi = True
+            _multi = submulti
+        else:
+            raise ValueError(_('invalid multi value'))
+        if _multi != 1:
+            _setattr(self, '_multi', _multi)
+        if multi is not False and default is None:
+            default = []
+        if validator is not None:
+            if multi:  # and validator_params is None:
+                validator_params = self._build_validator_params(validator, validator_params)
+
+            validate_callback(validator, validator_params, 'validator', self)
+            if validator_params is None:
+                val_call = (validator,)
+            else:
+                val_call = (validator, validator_params)
+            self._val_call = (val_call, None)
+            self._set_has_dependency()
+        if extra is not None:
+            _setattr(self, '_extra', extra)
+        if unique != undefined and not isinstance(unique, bool):
+            raise ValueError(_('unique must be a boolean'))
+        if not is_multi and unique is True:
+            raise ValueError(_('unique must be set only with multi value'))
+        if warnings_only is True:
+            _setattr(self, '_warnings_only', warnings_only)
+        if allow_empty_list is not undefined:
+            _setattr(self, '_allow_empty_list', allow_empty_list)
+
+        super(Option, self).__init__(name, doc, requires=requires,
+                                     properties=properties, is_multi=is_multi)
+        if is_multi and default_multi is not None:
+            err = self._validate(default_multi)
+            if err:
+                raise ValueError(_("invalid default_multi value {0} "
+                                   "for option {1}: {2}").format(
+                                       str(default_multi),
+                                       self.impl_getname(), str(err)))
+            _setattr(self, '_default_multi', default_multi)
+        if unique is not undefined:
+            _setattr(self, '_unique', unique)
+        err = self.impl_validate(default, is_multi=is_multi)
+        if err:
+            raise err
+        if (is_multi and default != []) or \
+                (not is_multi and default is not None):
+            if is_multi:
+                default = tuple(default)
+            _setattr(self, '_default', default)
+
+        self.impl_set_callback(callback, callback_params, _init=True)
+
+    def impl_is_multi(self):
+        return getattr(self, '_multi', 1) != 1
+
+    def _add_dependencies(self, option):
+        options = set(getattr(self, '_dependencies', tuple()))
+        options.add(option)
+        self._dependencies = tuple(options)
+
+    def _launch_consistency(self, current_opt, func, option, value, context,
+                            index, submulti_index, opts, warnings_only,
+                            transitive):
+        """Launch consistency now
+
+        :param func: function name, this name should start with _cons_
+        :type func: `str`
+        :param option: option that value is changing
+        :type option: `tiramisu.option.Option`
+        :param value: new value of this option
+        :param context: Config's context, if None, check default value instead
+        :type context: `tiramisu.config.Config`
+        :param index: only for multi option, consistency should be launch for
+                      specified index
+        :type index: `int`
+        :param opts: all options concerne by this consistency
+        :type opts: `list` of `tiramisu.option.Option`
+        :param warnings_only: specific raise error for warning
+        :type warnings_only: `boolean`
+        :param transitive: propertyerror is transitive
+        :type transitive: `boolean`
+        """
+        if context is not undefined:
+            descr = context.cfgimpl_get_description()
+
+        all_cons_vals = []
+        all_cons_opts = []
+        val_consistencies = True
+        for opt in opts:
+            if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \
+                    option == opt:
+                # option is current option
+                # we have already value, so use it
+                all_cons_vals.append(value)
+                all_cons_opts.append(opt)
+            else:
+                #if context, calculate value, otherwise get default value
+                path = None
+                is_multi = opt.impl_is_multi() and not opt.impl_is_master_slaves()
+                if context is not undefined:
+                    if isinstance(opt, DynSymLinkOption):
+                        path = opt.impl_getpath(context)
+                    else:
+                        path = descr.impl_get_path_by_opt(opt)
+                    if is_multi:
+                        _index = None
+                    else:
+                        _index = index
+                    opt_value = context.getattr(path, validate=False,
+                                                index=_index,
+                                                force_permissive=True,
+                                                returns_raise=True)
+                    if isinstance(opt_value, Exception):
+                        if isinstance(opt_value, PropertiesOptionError):
+                            if debug:  # pragma: no cover
+                                log.debug('propertyerror in _launch_consistency: {0}'.format(opt_value))
+                            if transitive:
+                                opt_value.set_orig_opt(option)
+                                return opt_value
+                            else:
+                                opt_value = None
+                        else:  # pragma: no cover
+                            return opt_value
+                elif index is None:
+                    opt_value = opt.impl_getdefault()
+                else:
+                    opt_value = opt.impl_getdefault()[index]
+
+                if self.impl_is_multi() and index is None:
+                    # only check propertyerror for master/slaves is transitive
+                    val_consistencies = False
+                if is_multi and isinstance(opt_value, list):
+                    all_cons_vals.extend(opt_value)
+                    for len_ in xrange(len(opt_value)):
+                        all_cons_opts.append(opt)
+                else:
+                    all_cons_vals.append(opt_value)
+                    all_cons_opts.append(opt)
+
+        if val_consistencies:
+            err = getattr(self, func)(current_opt, all_cons_opts, all_cons_vals, warnings_only)
+            if err:
+                if warnings_only:
+                    msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}').format(
+                        value, self._display_name, current_opt.impl_get_display_name(), err)
+                    warnings.warn_explicit(ValueWarning(msg, self),
+                                           ValueWarning,
+                                           self.__class__.__name__, 0)
+                else:
+                    return err
+
+    def impl_is_unique(self):
+        return getattr(self, '_unique', False)
+
+    def impl_get_validator(self):
+        val = getattr(self, '_val_call', (None,))[0]
+        if val is None:
+            ret_val = (None, {})
+        elif len(val) == 1:
+            ret_val = (val[0], {})
+        else:
+            ret_val = val
+        return ret_val
+
+    def impl_validate(self, value, context=undefined, validate=True,
+                      force_index=None, force_submulti_index=None,
+                      current_opt=undefined, is_multi=None,
+                      display_error=True, display_warnings=True, multi=None,
+                      setting_properties=undefined):
+        """
+        :param value: the option's value
+        :param context: Config's context
+        :type context: :class:`tiramisu.config.Config`
+        :param validate: if true enables ``self._validator`` validation
+        :type validate: boolean
+        :param force_index: if multi, value has to be a list
+                            not if force_index is not None
+        :type force_index: integer
+        :param force_submulti_index: if submulti, value has to be a list
+                                     not if force_submulti_index is not None
+        :type force_submulti_index: integer
+        """
+        if not validate:
+            return
+        if current_opt is undefined:
+            current_opt = self
+
+        if display_warnings and setting_properties is undefined and context is not undefined:
+            setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=False)
+        display_warnings = display_warnings and (setting_properties is undefined or 'warnings' in setting_properties)
+
+        def _is_not_unique(value):
+            if display_error and self.impl_is_unique() and len(set(value)) != len(value):
+                for idx, val in enumerate(value):
+                    if val in value[idx+1:]:
+                        return ValueError(_('invalid value "{}", this value is already in "{}"').format(
+                                            val, self.impl_get_display_name()))
+
+        def calculation_validator(val, _index):
+            validator, validator_params = self.impl_get_validator()
+            if validator is not None:
+                if validator_params != {}:
+                    validator_params_ = {}
+                    for val_param, values in validator_params.items():
+                        validator_params_[val_param] = values
+                    #inject value in calculation
+                    if '' in validator_params_:
+                        lst = list(validator_params_[''])
+                        lst.insert(0, val)
+                        validator_params_[''] = tuple(lst)
+                    else:
+                        validator_params_[''] = (val,)
+                else:
+                    validator_params_ = {'': (val,)}
+                # Raise ValueError if not valid
+                value = carry_out_calculation(current_opt, context=context,
+                                              callback=validator,
+                                              callback_params=validator_params_,
+                                              index=_index,
+                                              is_validator=True)
+                if isinstance(value, Exception):
+                    return value
+
+        def do_validation(_value, _index, submulti_index):
+            if _value is None:
+                error = warning = None
+            else:
+                if display_error:
+                    # option validation
+                    err = self._validate(_value, context, current_opt)
+                    if err:
+                        if debug:  # pragma: no cover
+                            log.debug('do_validation: value: {0}, index: {1}, '
+                                      'submulti_index: {2}'.format(_value, _index,
+                                                                   submulti_index),
+                                      exc_info=True)
+                        err_msg = '{0}'.format(err)
+                        if err_msg:
+                            msg = _('"{0}" is an invalid {1} for "{2}", {3}'
+                                    '').format(_value, self._display_name,
+                                               self.impl_get_display_name(), err_msg)
+                        else:
+                            msg = _('"{0}" is an invalid {1} for "{2}"'
+                                    '').format(_value, self._display_name,
+                                               self.impl_get_display_name())
+                        return ValueError(msg)
+                error = None
+                is_warnings_only = getattr(self, '_warnings_only', False)
+                if ((display_error and not is_warnings_only) or
+                        (display_warnings and is_warnings_only)):
+                    error = calculation_validator(_value, _index)
+                    if not error:
+                        error = self._second_level_validation(_value, is_warnings_only)
+                    if error:
+                        if debug:  # pragma: no cover
+                            log.debug(_('do_validation for {0}: error in value').format(
+                                self.impl_getname()), exc_info=True)
+                        if is_warnings_only:
+                            msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}').format(
+                                _value, self._display_name, self.impl_get_display_name(), error)
+                            warnings.warn_explicit(ValueWarning(msg, self),
+                                                   ValueWarning,
+                                                   self.__class__.__name__, 0)
+                            error = None
+            if error is None:
+                # if context launch consistency validation
+                #if context is not undefined:
+                ret = self._valid_consistency(current_opt, _value, context,
+                                              _index, submulti_index, display_warnings,
+                                              display_error)
+                if isinstance(ret, ValueError):
+                    error = ret
+                elif ret:
+                    return ret
+            if error:
+                err_msg = '{0}'.format(error)
+                if err_msg:
+                    msg = _('"{0}" is an invalid {1} for "{2}", {3}'
+                            '').format(_value, self._display_name,
+                                       self.impl_get_display_name(), err_msg)
+                else:
+                    msg = _('"{0}" is an invalid {1} for "{2}"'
+                            '').format(_value, self._display_name,
+                                       self.impl_get_display_name())
+                return ValueError(msg)
+
+        if is_multi is None:
+            is_multi = self.impl_is_multi()
+
+        if not is_multi:
+            return do_validation(value, None, None)
+        elif force_index is not None:
+            if self.impl_is_submulti() and force_submulti_index is None:
+                err = _is_not_unique(value)
+                if err:
+                    return err
+                if not isinstance(value, list):
+                    return ValueError(_('invalid value "{0}" for "{1}" which'
+                                        ' must be a list').format(
+                                           value, self.impl_get_display_name()))
+                for idx, val in enumerate(value):
+                    if isinstance(val, list):  # pragma: no cover
+                        return ValueError(_('invalid value "{}" for "{}" '
+                                            'which must not be a list').format(val,
+                                                                              self.impl_get_display_name()))
+                    err = do_validation(val, force_index, idx)
+                    if err:
+                        return err
+            else:
+                if multi is not None and self.impl_is_unique() and value in multi:
+                    if not self.impl_is_submulti() and len(multi) - 1 >= force_index:
+                        lst = list(multi)
+                        lst.pop(force_index)
+                    else:
+                        lst = multi
+                    if value in lst:
+                        return ValueError(_('invalid value "{}", this value is already'
+                                            ' in "{}"').format(value,
+                                                               self.impl_get_display_name()))
+                return do_validation(value, force_index, force_submulti_index)
+        elif not isinstance(value, list):
+            return ValueError(_('invalid value "{0}" for "{1}" which '
+                                'must be a list').format(value,
+                                                         self.impl_getname()))
+        elif self.impl_is_submulti() and force_submulti_index is None:
+            for idx, val in enumerate(value):
+                err = _is_not_unique(val)
+                if err:
+                    return err
+                if not isinstance(val, list):
+                    return ValueError(_('invalid value "{0}" for "{1}" '
+                                        'which must be a list of list'
+                                        '').format(val,
+                                                   self.impl_getname()))
+                for slave_idx, slave_val in enumerate(val):
+                    err = do_validation(slave_val, idx, slave_idx)
+                    if err:
+                        return err
+        else:
+            err = _is_not_unique(value)
+            if err:
+                return err
+            for idx, val in enumerate(value):
+                err = do_validation(val, idx, force_submulti_index)
+                if err:
+                    return err
+            return self._valid_consistency(current_opt, None, context,
+                                           None, None, display_warnings, display_error)
+
+    def impl_is_dynsymlinkoption(self):
+        return False
+
+    def impl_is_master_slaves(self, type_='both'):
+        """FIXME
+        """
+        master_slaves = self.impl_get_master_slaves()
+        if master_slaves is not None:
+            if type_ in ('both', 'master') and \
+                    master_slaves.is_master(self):
+                return True
+            if type_ in ('both', 'slave') and \
+                    not master_slaves.is_master(self):
+                return True
+        return False
+
+    def impl_get_master_slaves(self):
+        return getattr(self, '_master_slaves', None)
+
+    def impl_getdoc(self):
+        "accesses the Option's doc"
+        return self.impl_get_information('doc')
+
+    def _valid_consistencies(self, other_opts, init=True, func=None):
+        if self._is_subdyn():
+            dynod = self._subdyn
+        else:
+            dynod = None
+        if self.impl_is_submulti():
+            raise ConfigError(_('cannot add consistency with submulti option'))
+        is_multi = self.impl_is_multi()
+        for opt in other_opts:
+            if opt.impl_is_submulti():
+                raise ConfigError(_('cannot add consistency with submulti option'))
+            if not isinstance(opt, Option):
+                raise ConfigError(_('consistency must be set with an option'))
+            if opt._is_subdyn():
+                if dynod is None:
+                    raise ConfigError(_('almost one option in consistency is '
+                                        'in a dynoptiondescription but not all'))
+                if dynod != opt._subdyn:
+                    raise ConfigError(_('option in consistency must be in same'
+                                        ' dynoptiondescription'))
+                dynod = opt._subdyn
+            elif dynod is not None:
+                raise ConfigError(_('almost one option in consistency is in a '
+                                    'dynoptiondescription but not all'))
+            if self is opt:
+                raise ConfigError(_('cannot add consistency with itself'))
+            if is_multi != opt.impl_is_multi():
+                raise ConfigError(_('every options in consistency must be '
+                                    'multi or none'))
+            if init:
+                # FIXME
+                if func != 'not_equal':
+                    opt._set_has_dependency()
+
+    def impl_add_consistency(self, func, *other_opts, **params):
+        """Add consistency means that value will be validate with other_opts
+        option's values.
+
+        :param func: function's name
+        :type func: `str`
+        :param other_opts: options used to validate value
+        :type other_opts: `list` of `tiramisu.option.Option`
+        :param params: extra params (warnings_only and transitive are allowed)
+        """
+        if self.impl_is_readonly(): 
+            raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is"
+                                   " read-only").format(
+                                       self.__class__.__name__,
+                                       self.impl_getname()))
+        self._valid_consistencies(other_opts, func=func)
+        func = '_cons_{0}'.format(func)
+        if func not in dir(self):
+            raise ConfigError(_('consistency {0} not available for this option').format(func))
+        all_cons_opts = tuple([self] + list(other_opts))
+        unknown_params = set(params.keys()) - set(['warnings_only', 'transitive'])
+        if unknown_params != set():
+            raise ValueError(_('unknow parameter {0} in consistency').format(unknown_params))
+        self._add_consistency(func, all_cons_opts, params)
+        #validate default value when add consistency
+        err = self.impl_validate(self.impl_getdefault())
+        if err:
+            self._del_consistency()
+            raise err
+        if func in ALLOWED_CONST_LIST:
+            for opt in all_cons_opts:
+                if getattr(opt, '_unique', undefined) == undefined:
+                    opt._unique = True
+        if func != '_cons_not_equal':
+            #consistency could generate warnings or errors
+            self._set_has_dependency()
+
+    def _valid_consistency(self, option, value, context, index, submulti_idx,
+                           display_warnings, display_error):
+        if context is not undefined:
+            descr = context.cfgimpl_get_description()
+            if descr._cache_consistencies is None:
+                return
+            #consistencies is something like [('_cons_not_equal', (opt1, opt2))]
+            if isinstance(option, DynSymLinkOption):
+                consistencies = descr._cache_consistencies.get(option._impl_getopt())
+            else:
+                consistencies = descr._cache_consistencies.get(option)
+        else:
+            consistencies = option._get_consistencies()
+        if consistencies is not None:
+            for func, all_cons_opts, params in consistencies:
+                warnings_only = params.get('warnings_only', False)
+                if (warnings_only and display_warnings) or (not warnings_only and display_error):
+                    transitive = params.get('transitive', True)
+                    #all_cons_opts[0] is the option where func is set
+                    if isinstance(option, DynSymLinkOption):
+                        subpath = '.'.join(option._dyn.split('.')[:-1])
+                        namelen = len(option._impl_getopt().impl_getname())
+                        suffix = option.impl_getname()[namelen:]
+                        opts = []
+                        for opt in all_cons_opts:
+                            name = opt.impl_getname() + suffix
+                            path = subpath + '.' + name
+                            opts.append(opt._impl_to_dyn(name, path))
+                    else:
+                        opts = all_cons_opts
+                    err = opts[0]._launch_consistency(self, func, option, value,
+                                                      context, index, submulti_idx,
+                                                      opts, warnings_only,
+                                                      transitive)
+                    if err:
+                        return err
+
+    def _cons_not_equal(self, current_opt, opts, vals, warnings_only):
+        equal = set()
+        is_current = False
+        for idx_inf, val_inf in enumerate(vals):
+            for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
+                if val_inf == val_sup is not None:
+                    for opt_ in [opts[idx_inf], opts[idx_inf + idx_sup + 1]]:
+                        if opt_ == current_opt:
+                            is_current = True
+                        else:
+                            equal.add(opt_)
+        if equal:
+            if debug:  # pragma: no cover
+                log.debug(_('_cons_not_equal: {} are not different').format(display_list(list(equal))))
+            if is_current:
+                if warnings_only:
+                    msg = _('should be different from the value of {}')
+                else:
+                    msg = _('must be different from the value of {}')
+            else:
+                if warnings_only:
+                    msg = _('value for {} should be different')
+                else:
+                    msg = _('value for {} must be different')
+            equal_name = []
+            for opt in equal:
+                equal_name.append(opt.impl_get_display_name())
+            return ValueError(msg.format(display_list(list(equal_name))))
+
+    def _second_level_validation(self, value, warnings_only):
+        pass
+
+    def _impl_to_dyn(self, name, path):
+        return DynSymLinkOption(name, self, dyn=path)
+
+    def impl_getdefault_multi(self):
+        "accessing the default value for a multi"
+        return getattr(self, '_default_multi', None)
+
+    def _validate_callback(self, callback, callback_params):
+        """callback_params:
+        * None
+        * {'': ((option, permissive),), 'ip': ((None,), (option, permissive))
+        """
+        if callback is None:
+            return
+        default_multi = self.impl_getdefault_multi()
+        is_multi = self.impl_is_multi()
+        default = self.impl_getdefault()
+        if (not is_multi and (default is not None or default_multi is not None)) or \
+                (is_multi and (default != [] or default_multi is not None)):
+            raise ValueError(_("default value not allowed if option: {0} "
+                             "is calculated").format(self.impl_getname()))
+
+    def impl_getdefault(self):
+        "accessing the default value"
+        is_multi = self.impl_is_multi()
+        default = getattr(self, '_default', undefined)
+        if default is undefined:
+            if is_multi:
+                default = []
+            else:
+                default = None
+        else:
+            if is_multi:
+                default = list(default)
+        return default
+
+    def _get_extra(self, key):
+        extra = self._extra
+        if isinstance(extra, tuple):
+            return extra[1][extra[0].index(key)]
+        else:
+            return extra[key]
+
+    def impl_is_submulti(self):
+        return getattr(self, '_multi', 1) == 2
+
+    def impl_allow_empty_list(self):
+        return getattr(self, '_allow_empty_list', undefined)
+
+    #____________________________________________________________
+    # consistency
+    def _add_consistency(self, func, all_cons_opts, params):
+        cons = (func, all_cons_opts, params)
+        consistencies = getattr(self, '_consistencies', None)
+        if consistencies is None:
+            self._consistencies = [cons]
+        else:
+            consistencies.append(cons)
+
+    def _del_consistency(self):
+        self._consistencies.pop(-1)
+
+    def _get_consistencies(self):
+        return getattr(self, '_consistencies', STATIC_TUPLE)
+
+    def _has_consistencies(self):
+        return hasattr(self, '_consistencies')
 
 
 class _RegexpOption(Option):
index 9780828..d6d9e3b 100644 (file)
@@ -24,13 +24,14 @@ import re
 
 from ..i18n import _
 from ..setting import groups, undefined, owners  # , log
-from .baseoption import BaseOption, SymLinkOption, Option, ALLOWED_CONST_LIST
+from .baseoption import BaseOption
+from .option import Option, ALLOWED_CONST_LIST
 from . import MasterSlaves
 from ..error import ConfigError, ConflictError
 from ..autolib import carry_out_calculation
 
 
-name_regexp = re.compile(r'^[a-zA-Z\d\-_]*$')
+NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
 
 import sys
 if sys.version_info[0] >= 3:  # pragma: no cover
@@ -76,7 +77,7 @@ class CacheOptionDescription(BaseOption):
                     option._dependencies = tuple(options)
                 option._set_readonly(True)
                 is_multi = option.impl_is_multi()
-                if not isinstance(option, SymLinkOption) and 'force_store_value' in option.impl_getproperties():
+                if not option._is_symlinkoption() and 'force_store_value' in option.impl_getproperties():
                     force_store_values.append((subpath, option))
                 for func, all_cons_opts, params in option._get_consistencies():
                     option._valid_consistencies(all_cons_opts[1:], init=False)
@@ -458,7 +459,7 @@ class OptionDescription(OptionDescriptionWalk):
             if isinstance(group_type, groups.MasterGroupType):
                 children = self.impl_getchildren()
                 for child in children:
-                    if isinstance(child, SymLinkOption):  # pragma: optional cover
+                    if child._is_symlinkoption():  # pragma: optional cover
                         raise ValueError(_("master group {0} shall not have "
                                            "a symlinkoption").format(self.impl_getname()))
                     if not isinstance(child, Option):  # pragma: optional cover
@@ -490,7 +491,7 @@ class OptionDescription(OptionDescriptionWalk):
         if len(values) > len(set(values)):
             raise ConfigError(_('DynOptionDescription callback return not unique value'))
         for val in values:
-            if not isinstance(val, str) or re.match(name_regexp, val) is None:
+            if not isinstance(val, str) or re.match(NAME_REGEXP, val) is None:
                 raise ValueError(_("invalid suffix: {0} for option").format(val))
         return values
 
@@ -507,7 +508,7 @@ class DynOptionDescription(OptionDescription):
                                         'dynoptiondescription'))
                 for chld in child._impl_getchildren():
                     chld._impl_setsubdyn(self)
-            if isinstance(child, SymLinkOption):
+            if child._is_symlinkoption():
                 raise ConfigError(_('cannot set symlinkoption in a '
                                     'dynoptiondescription'))
             child._impl_setsubdyn(self)
index 57e5a2d..15b8a86 100644 (file)
@@ -21,7 +21,7 @@
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class PasswordOption(Option):
index db851b0..c810d36 100644 (file)
@@ -23,7 +23,7 @@ import sys
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class PortOption(Option):
index 9a8c884..c4b79ee 100644 (file)
@@ -22,7 +22,7 @@ import sys
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 
 
 class StrOption(Option):
index 5574641..65daf6f 100644 (file)
@@ -22,7 +22,7 @@ import re
 
 from ..setting import undefined
 from ..i18n import _
-from .baseoption import Option
+from .option import Option
 from .domainnameoption import DomainnameOption
 
 
index dcb226d..4bd0939 100644 (file)
@@ -22,8 +22,7 @@ from .error import ConfigError, SlaveError, PropertiesOptionError
 from .setting import owners, expires_time, undefined
 from .autolib import carry_out_calculation
 from .i18n import _
-from .option import SymLinkOption, DynSymLinkOption, Option
-i_i = 0
+from .option import DynSymLinkOption, Option
 
 
 class Values(object):
@@ -497,7 +496,7 @@ class Values(object):
                                  was present
         :returns: a `setting.owners.Owner` object
         """
-        if isinstance(opt, SymLinkOption) and \
+        if opt._is_symlinkoption() and \
                 not isinstance(opt, DynSymLinkOption):
             opt = opt._impl_getopt()
         path = opt.impl_getpath(self._getcontext())
@@ -647,7 +646,7 @@ class Values(object):
                         for path in _mandatory_warnings(opt, currpath + [name]):
                             yield path
                 else:
-                    if isinstance(opt, SymLinkOption) and \
+                    if opt._is_symlinkoption() and \
                             not isinstance(opt, DynSymLinkOption):
                         continue
                     self_properties = settings._getproperties(opt, path,