separate baseoption and option

This commit is contained in:
Emmanuel Garette 2017-07-24 20:39:01 +02:00
parent 3c9a759e2e
commit 635b71d291
20 changed files with 682 additions and 669 deletions

View 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,

View 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

View File

@ -20,14 +20,11 @@
# ____________________________________________________________
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,633 +393,14 @@ class BaseOption(Base):
opt=self,
path=path)
def _is_symlinkoption(self):
return False
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):
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')
def validate_requires_arg(new_option, multi, requires, name):
"""check malformed requirements
and tranform dict to internal tuple
@ -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)

View File

@ -21,7 +21,7 @@
from ..setting import undefined
from ..i18n import _
from .baseoption import Option
from .option import Option
class BoolOption(Option):

View 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):

View 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):

View File

@ -18,12 +18,11 @@
# 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):

View 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):

View File

@ -21,7 +21,7 @@
from ..setting import undefined
from ..i18n import _
from .baseoption import Option
from .option import Option
class FloatOption(Option):

View File

@ -21,7 +21,7 @@
from ..setting import undefined
from ..i18n import _
from .baseoption import Option
from .option import Option
class IntOption(Option):

View 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):

View 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):

View 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):

View File

@ -19,9 +19,642 @@
# 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):

View 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)

View File

@ -21,7 +21,7 @@
from ..setting import undefined
from ..i18n import _
from .baseoption import Option
from .option import Option
class PasswordOption(Option):

View 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):

View 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):

View 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

View 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,