tiramisu/tiramisu/option.py

742 lines
29 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"option types and option description for the configuration management"
2013-02-21 17:07:00 +01:00
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
2012-10-05 16:00:07 +02:00
# The original `Config` design model is unproudly borrowed from
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
2013-02-25 15:52:10 +01:00
import re
2013-04-03 12:20:26 +02:00
from copy import copy
2012-11-19 09:51:40 +01:00
from types import FunctionType
from IPy import IP
from tiramisu.error import ConflictError
from tiramisu.setting import groups, multitypes
2013-04-13 23:09:05 +02:00
from tiramisu.i18n import _
2012-10-15 15:06:41 +02:00
2013-02-25 15:52:10 +01:00
name_regexp = re.compile(r'^\d+')
forbidden_names = ['iter_all', 'iter_group', 'find', 'find_fisrt',
'make_dict', 'unwrap_from_path', 'read_only',
'read_write', 'getowner']
2013-02-25 15:52:10 +01:00
2013-04-03 12:20:26 +02:00
2013-02-25 15:52:10 +01:00
def valid_name(name):
2013-03-06 09:22:56 +01:00
try:
name = str(name)
except:
2013-04-14 12:01:32 +02:00
return False
if re.match(name_regexp, name) is None and not name.startswith('_') \
and name not in forbidden_names \
and not name.startswith('optimpl_') and \
not name.startswith('cfgimpl_'):
2013-02-25 16:06:10 +01:00
return True
else:
return False
#____________________________________________________________
#
2013-02-25 16:06:10 +01:00
2013-04-03 12:20:26 +02:00
class BaseInformation(object):
__slots__ = ('_informations')
def optimpl_set_information(self, key, value):
"""updates the information's attribute
(wich is a dictionnary)
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
self._informations[key] = value
def optimpl_get_information(self, key, default=None):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
if key in self._informations:
return self._informations[key]
2013-04-03 12:20:26 +02:00
elif default is not None:
return default
else:
2013-04-13 23:09:05 +02:00
raise ValueError(_("Information's item not found: {0}").format(key))
2013-04-03 12:20:26 +02:00
class Option(BaseInformation):
2012-10-05 16:00:07 +02:00
"""
2012-11-20 17:14:58 +01:00
Abstract base class for configuration option's.
Reminder: an Option object is **not** a container for the value
2012-10-05 16:00:07 +02:00
"""
__slots__ = ('_name', '_requires', '_multi', '_validator', '_default_multi',
'_default', '_properties', '_callback', '_multitype',
'_master_slaves', '_consistencies', '_empty')
2013-04-17 22:06:10 +02:00
_empty = ''
2013-04-03 12:20:26 +02:00
2012-10-05 16:00:07 +02:00
def __init__(self, name, doc, default=None, default_multi=None,
2013-04-03 12:20:26 +02:00
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
properties=None):
2012-11-12 12:06:58 +01:00
"""
2012-11-20 17:14:58 +01:00
:param name: the option's name
:param doc: the option's description
:param default: specifies the default value of the option,
for a multi : ['bla', 'bla', 'bla']
2012-11-12 12:06:58 +01:00
:param default_multi: 'bla' (used in case of a reset to default only at
2012-11-20 17:14:58 +01:00
a given index)
:param requires: is a list of names of options located anywhere
in the configuration.
:param multi: if true, the option's value is a list
:param callback: the name of a function. If set, the function's output
is responsible of the option's value
:param callback_params: the callback's parameter
:param validator: the name of a function wich stands for a custom
validation of the value
:param validator_args: the validator's parameters
2012-11-12 12:06:58 +01:00
"""
2013-02-25 16:06:10 +01:00
if not valid_name(name):
2013-04-14 12:01:32 +02:00
raise ValueError(_("invalid name: {0} for option").format(name))
self._name = name
self._informations = {}
self.optimpl_set_information('doc', doc)
2013-04-03 12:20:26 +02:00
validate_requires_arg(requires, self._name)
self._requires = requires
self._multi = multi
2013-04-14 10:14:06 +02:00
self._consistencies = None
2012-11-19 09:51:40 +01:00
if validator is not None:
if type(validator) != FunctionType:
2013-04-13 23:09:05 +02:00
raise TypeError(_("validator must be a function"))
if validator_args is None:
validator_args = {}
2013-04-03 12:20:26 +02:00
self._validator = (validator, validator_args)
else:
self._validator = None
if not self._multi and default_multi is not None:
2013-04-14 12:01:32 +02:00
raise ValueError(_("a default_multi is set whereas multi is False"
" in option: {0}").format(name))
if default_multi is not None and not self._validate(default_multi):
2013-04-14 12:01:32 +02:00
raise ValueError(_("invalid default_multi value {0} "
"for option {1}").format(str(default_multi), name))
if callback is not None and (default is not None or default_multi is not None):
2013-04-14 12:01:32 +02:00
raise ValueError(_("defaut values not allowed if option: {0} "
"is calculated").format(name))
2013-04-03 12:20:26 +02:00
if callback is None and callback_params is not None:
2013-04-14 12:01:32 +02:00
raise ValueError(_("params defined for a callback function but "
"no callback defined yet for option {0}").format(name))
2013-04-03 12:20:26 +02:00
if callback is not None:
if not isinstance(callback, str):
raise ValueError('callback must be a string')
if callback_params is not None and \
not isinstance(callback_params, dict):
raise ValueError('callback_params must be a dict')
self._callback = (callback, callback_params)
2013-04-03 12:20:26 +02:00
else:
self._callback = None
if self._multi:
2013-04-03 12:20:26 +02:00
if default is None:
default = []
#if not isinstance(default, list):
# raise ValidateError("invalid default value {0} "
# "for option {1} : not list type"
# "".format(str(default), name))
if not self.optimpl_validate(default):
2013-04-14 12:01:32 +02:00
raise ValueError(_("invalid default value {0} "
"for option {1}"
"").format(str(default), name))
self._multitype = multitypes.default
self._default_multi = default_multi
else:
if default is not None and not self.optimpl_validate(default):
2013-04-14 12:01:32 +02:00
raise ValueError(_("invalid default value {0} "
"for option {1}").format(str(default), name))
self._default = default
2013-04-03 12:20:26 +02:00
if properties is None:
properties = ()
if not isinstance(properties, tuple):
2013-04-14 12:01:32 +02:00
raise TypeError(_('invalid properties type {0} for {1},'
' must be a tuple').format(type(properties), self._name))
2013-04-03 12:20:26 +02:00
self._properties = properties # 'hidden', 'disabled'...
2012-10-05 16:00:07 +02:00
def optimpl_validate(self, value, context=None, validate=True):
2012-11-19 09:51:40 +01:00
"""
:param value: the option's value
:param validate: if true enables ``self._validator`` validation
"""
# generic calculation
if context is not None:
cons = context.cfgimpl_get_description()
else:
cons = None
if not self._multi:
# None allows the reset of the value
2013-04-03 12:20:26 +02:00
if value is not None:
2012-11-22 11:53:51 +01:00
# customizing the validator
if validate and self._validator is not None and \
2013-04-03 12:20:26 +02:00
not self._validator[0](value, **self._validator[1]):
2012-11-22 11:53:51 +01:00
return False
if not self._validate(value):
return False
if cons is not None:
return cons._valid_consistency(self, value, context, None)
else:
2012-10-05 16:00:07 +02:00
if not isinstance(value, list):
2013-04-14 12:01:32 +02:00
raise ValueError(_("invalid value {0} "
2013-04-16 09:34:00 +02:00
"for option {1} which must be a list"
"").format(value, self._name))
for index in range(0, len(value)):
val = value[index]
2012-11-22 11:53:51 +01:00
# None allows the reset of the value
2013-04-03 12:20:26 +02:00
if val is not None:
2012-11-22 11:53:51 +01:00
# customizing the validator
if validate and self._validator is not None and \
2013-04-03 12:20:26 +02:00
not self._validator[0](val, **self._validator[1]):
2012-11-22 11:53:51 +01:00
return False
if not self._validate(val):
return False
if cons is not None and not cons._valid_consistency(self, val, context, index):
return False
return True
def optimpl_getdefault(self, default_multi=False):
2012-10-05 16:00:07 +02:00
"accessing the default value"
if not default_multi or not self.optimpl_is_multi():
return self._default
2012-11-29 11:40:52 +01:00
else:
return self.getdefault_multi()
def optimpl_getdefault_multi(self):
2012-11-15 14:59:36 +01:00
"accessing the default value for a multi"
return self._default_multi
def optimpl_get_multitype(self):
return self._multitype
def optimpl_get_master_slaves(self):
return self._master_slaves
2012-11-15 14:59:36 +01:00
def optimpl_is_empty_by_default(self):
2012-10-05 16:00:07 +02:00
"no default value has been set yet"
if ((not self.optimpl_is_multi() and self._default is None) or
(self.optimpl_is_multi() and (self._default == [] or None in self._default))):
2012-09-11 15:18:38 +02:00
return True
return False
def optimpl_getdoc(self):
2012-10-05 16:00:07 +02:00
"accesses the Option's doc"
return self.optimpl_get_information('doc')
2012-10-05 16:00:07 +02:00
def optimpl_has_callback(self):
2012-10-05 16:00:07 +02:00
"to know if a callback has been defined or not"
if self._callback is None:
2012-07-27 11:46:27 +02:00
return False
else:
return True
def optimpl_getkey(self, value):
return value
2013-04-03 12:20:26 +02:00
def optimpl_is_multi(self):
return self._multi
def optimpl_add_consistency(self, func, opts):
2013-04-16 09:34:00 +02:00
pass
if self._consistencies is None:
self._consistencies = []
if self not in opts:
opts = list(opts)
opts.append(self)
opts = tuple(opts)
self._consistencies.append(('_cons_{}'.format(func), opts))
2013-04-16 09:34:00 +02:00
def _cons_not_equal(self, opt, value, context, index, opts):
values = [value]
descr = context.cfgimpl_get_description()
for opt_ in opts:
if opt_ is not opt:
path = descr.optimpl_get_path_by_opt(opt_)
val = context._getattr(path, validate=False)
2013-04-14 10:14:06 +02:00
if val is not None:
if val in values:
return False
values.append(val)
return True
2013-04-03 12:20:26 +02:00
def _cons_lower(self, value):
2013-04-16 09:34:00 +02:00
try:
return value.islower()
except AttributeError:
#no "islower" attribute
return False
2013-04-14 10:14:06 +02:00
2012-10-05 16:00:07 +02:00
class ChoiceOption(Option):
__slots__ = ('_values', '_open_values', '_opt_type')
_opt_type = 'string'
2012-10-05 16:00:07 +02:00
2012-11-16 10:04:25 +01:00
def __init__(self, name, doc, values, default=None, default_multi=None,
2013-04-03 12:20:26 +02:00
requires=None, multi=False, callback=None,
2012-11-19 09:51:40 +01:00
callback_params=None, open_values=False, validator=None,
2013-04-03 12:20:26 +02:00
validator_args=None, properties=()):
if not isinstance(values, tuple):
2013-04-14 12:01:32 +02:00
raise TypeError(_('values must be a tuple for {0}').format(name))
self._values = values
2013-04-03 12:20:26 +02:00
if open_values not in (True, False):
2013-04-14 12:01:32 +02:00
raise TypeError(_('open_values must be a boolean for '
'{0}').format(name))
self._open_values = open_values
super(ChoiceOption, self).__init__(name, doc, default=default,
2013-04-03 12:20:26 +02:00
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
properties=properties)
def _validate(self, value):
if not self._open_values:
return value is None or value in self._values
2012-07-23 14:30:06 +02:00
else:
return True
2013-04-03 12:20:26 +02:00
class BoolOption(Option):
__slots__ = ('_opt_type')
_opt_type = 'bool'
2012-10-05 16:00:07 +02:00
def _validate(self, value):
return isinstance(value, bool)
2013-04-03 12:20:26 +02:00
class IntOption(Option):
__slots__ = ('_opt_type')
_opt_type = 'int'
2012-10-05 16:00:07 +02:00
def _validate(self, value):
2012-09-19 09:31:02 +02:00
return isinstance(value, int)
2013-04-03 12:20:26 +02:00
class FloatOption(Option):
__slots__ = ('_opt_type')
_opt_type = 'float'
def _validate(self, value):
2012-09-19 09:31:02 +02:00
return isinstance(value, float)
2013-04-03 12:20:26 +02:00
class StrOption(Option):
__slots__ = ('_opt_type')
_opt_type = 'string'
2012-10-05 16:00:07 +02:00
def _validate(self, value):
return isinstance(value, str)
2012-10-05 16:00:07 +02:00
2013-04-03 12:20:26 +02:00
2013-03-20 12:37:27 +01:00
class UnicodeOption(Option):
__slots__ = ('_opt_type')
_opt_type = 'unicode'
2013-04-17 22:06:10 +02:00
_empty = u''
2013-03-20 12:37:27 +01:00
def _validate(self, value):
return isinstance(value, unicode)
2013-04-03 12:20:26 +02:00
class SymLinkOption(object):
__slots__ = ('_name', '_opt', '_consistencies')
_opt_type = 'symlink'
2013-04-14 10:14:06 +02:00
_consistencies = None
2012-10-05 16:00:07 +02:00
2012-11-30 16:23:40 +01:00
def __init__(self, name, path, opt):
self._name = name
self._opt = opt
2012-10-05 16:00:07 +02:00
2012-11-30 15:08:34 +01:00
def __getattr__(self, name):
if name in ('_name', '_opt', '_consistencies'):
2013-04-03 12:20:26 +02:00
return object.__gettattr__(self, name)
2012-11-30 16:23:40 +01:00
else:
return getattr(self._opt, name)
2012-11-30 15:08:34 +01:00
2013-04-03 12:20:26 +02:00
class IPOption(Option):
__slots__ = ('_opt_type', '_only_private')
_opt_type = 'ip'
2012-10-05 16:00:07 +02:00
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
properties=None, only_private=False):
super(IPOption, self).__init__(name, doc, default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
properties=properties)
self._only_private = only_private
def _validate(self, value):
try:
ip = IP('{0}/32'.format(value))
if self._only_private:
return ip.iptype() == 'PRIVATE'
return True
except ValueError:
return False
class NetworkOption(Option):
__slots__ = ('_opt_type')
_opt_type = 'network'
def _validate(self, value):
try:
IP(value)
return True
except ValueError:
return False
2012-10-05 16:00:07 +02:00
2013-04-03 12:20:26 +02:00
class NetmaskOption(Option):
__slots__ = ('_opt_type')
_opt_type = 'netmask'
2012-10-05 16:00:07 +02:00
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
properties=None, opt_ip=None):
if opt_ip is not None and not isinstance(opt_ip, IPOption) and \
not isinstance(opt_ip, NetworkOption):
2013-04-14 12:01:32 +02:00
raise TypeError(_('opt_ip must be a IPOption not {}').format(type(opt_ip)))
super(NetmaskOption, self).__init__(name, doc, default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
properties=properties)
if opt_ip is None:
pass
elif isinstance(opt_ip, IPOption):
2013-04-14 10:14:06 +02:00
self._consistencies = [('cons_ip_netmask', (self, opt_ip))]
elif isinstance(opt_ip, NetworkOption):
2013-04-14 10:14:06 +02:00
self._consistencies = [('cons_network_netmask', (self, opt_ip))]
else:
2013-04-14 12:01:32 +02:00
raise TypeError(_('unknown type for opt_ip'))
def _validate(self, value):
try:
IP('0.0.0.0/{}'.format(value))
return True
except ValueError:
return False
def _cons_network_netmask(self, opt, value, context, index, opts):
#opts must be (netmask, network) options
return self._cons_netmask(opt, value, context, index, opts, False)
def _cons_ip_netmask(self, opt, value, context, index, opts):
#opts must be (netmask, ip) options
return self._cons_netmask(opt, value, context, index, opts, True)
def __cons_netmask(self, opt, value, context, index, opts, make_net):
opt_netmask, opt_ipnetwork = opts
descr = context.cfgimpl_get_description()
if opt is opt_ipnetwork:
val_ipnetwork = value
path = descr.optimpl_get_path_by_opt(opt_netmask)
val_netmask = context._getattr(path, validate=False)
if opt_netmask.optimpl_is_multi():
val_netmask = val_netmask[index]
if val_netmask is None:
return True
else:
val_netmask = value
path = descr.optimpl_get_path_by_opt(opt_ipnetwork)
val_ipnetwork = getattr(context, path)
if opt_ipnetwork.optimpl_is_multi():
val_ipnetwork = val_ipnetwork[index]
if val_ipnetwork is None:
return True
try:
IP('{}/{}'.format(val_ipnetwork, val_netmask, make_net=make_net))
return True
except ValueError:
return False
2012-10-05 16:00:07 +02:00
2013-04-03 12:20:26 +02:00
2013-04-16 09:34:00 +02:00
class DomainnameOption(Option):
__slots__ = ('_opt_type', '_type', '_allow_ip')
_opt_type = 'domainname'
2013-04-16 09:34:00 +02:00
#allow_ip
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
properties=None, allow_ip=False, type_='domainname'):
#netbios: for MS domain
#hostname: to identify the device
#domainname:
#fqdn: with tld, not supported yet
super(NetmaskOption, self).__init__(name, doc, default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
properties=properties)
if type_ not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
self._type = type_
self._allow_ip = allow_ip
def _validate(self, value):
if self._allow_ip is True:
try:
IP('{0}/32'.format(value))
return True
except ValueError:
pass
if self._type == 'netbios':
length = 15
extrachar = ''
elif self._type == 'hostname':
length = 63
extrachar = ''
elif self._type == 'domainname':
length = 255
extrachar = '\.'
regexp = r'^[a-zA-Z]([a-zA-Z\d-{0}]{{,{1}}})*[a-zA-Z\d]$'.format(
extrachar, length - 2)
return re.match(regexp, value) is not None
2013-04-03 12:20:26 +02:00
class OptionDescription(BaseInformation):
2012-12-06 18:14:57 +01:00
"""Config's schema (organisation, group) and container of Options"""
2013-04-03 12:20:26 +02:00
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
'_properties', '_children', '_consistencies')
2013-04-03 12:20:26 +02:00
def __init__(self, name, doc, children, requires=None, properties=()):
2012-10-05 16:00:07 +02:00
"""
:param children: is a list of option descriptions (including
``OptionDescription`` instances for nested namespaces).
"""
2013-02-25 16:06:10 +01:00
if not valid_name(name):
2013-04-14 12:01:32 +02:00
raise ValueError(_("invalid name: {0} for option descr").format(name))
self._name = name
self._informations = {}
self.optimpl_set_information('doc', doc)
2013-04-03 12:20:26 +02:00
child_names = [child._name for child in children]
#better performance like this
valid_child = copy(child_names)
valid_child.sort()
old = None
for child in valid_child:
if child == old:
raise ConflictError(_('duplicate option name: '
'{0}').format(child))
2013-04-03 12:20:26 +02:00
old = child
self._children = (tuple(child_names), tuple(children))
validate_requires_arg(requires, self._name)
self._requires = requires
self._cache_paths = None
self._consistencies = None
2013-04-03 12:20:26 +02:00
if not isinstance(properties, tuple):
2013-04-14 12:01:32 +02:00
raise TypeError(_('invalid properties type {0} for {1},'
2013-04-13 23:09:05 +02:00
' must be a tuple').format(type(properties), self._name))
2013-04-03 12:20:26 +02:00
self._properties = properties # 'hidden', 'disabled'...
# the group_type is useful for filtering OptionDescriptions in a config
self._group_type = groups.default
2012-10-05 16:00:07 +02:00
def optimpl_getdoc(self):
return self.optimpl_get_information('doc')
2013-04-03 12:20:26 +02:00
def __getattr__(self, name):
2013-04-04 11:24:00 +02:00
try:
2013-04-03 12:20:26 +02:00
return self._children[1][self._children[0].index(name)]
2013-04-04 11:24:00 +02:00
except ValueError:
2013-04-13 23:09:05 +02:00
raise AttributeError(_('unknown Option {} in OptionDescription {}'
'').format(name, self._name))
2012-10-05 16:00:07 +02:00
def optimpl_getkey(self, config):
return tuple([child.optimpl_getkey(getattr(config, child._name))
for child in self.optimpl_getchildren()])
def optimpl_getpaths(self, include_groups=False, _currpath=None):
"""returns a list of all paths in self, recursively
2013-04-04 11:24:00 +02:00
_currpath should not be provided (helps with recursion)
"""
2013-04-04 11:24:00 +02:00
if _currpath is None:
_currpath = []
paths = []
for option in self.optimpl_getchildren():
attr = option._name
2012-09-24 15:58:37 +02:00
if isinstance(option, OptionDescription):
if include_groups:
2013-04-04 11:24:00 +02:00
paths.append('.'.join(_currpath + [attr]))
paths += option.optimpl_getpaths(include_groups=include_groups,
_currpath=_currpath + [attr])
else:
2013-04-04 11:24:00 +02:00
paths.append('.'.join(_currpath + [attr]))
return paths
def optimpl_getchildren(self):
2013-04-04 11:24:00 +02:00
return self._children[1]
def optimpl_build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
2013-04-04 11:24:00 +02:00
if _currpath is None and self._cache_paths is not None:
return
2013-04-04 11:24:00 +02:00
if _currpath is None:
2013-04-03 12:20:26 +02:00
save = True
2013-04-04 11:24:00 +02:00
_currpath = []
_consistencies = {}
2013-04-03 12:20:26 +02:00
else:
save = False
if cache_path is None:
2013-04-04 11:24:00 +02:00
cache_path = [self._name]
cache_option = [self]
for option in self.optimpl_getchildren():
attr = option._name
if attr.startswith('_cfgimpl'):
continue
2013-04-03 12:20:26 +02:00
cache_option.append(option)
2013-04-04 11:24:00 +02:00
cache_path.append(str('.'.join(_currpath + [attr])))
if not isinstance(option, OptionDescription):
2013-04-14 10:14:06 +02:00
if option._consistencies is not None:
for consistency in option._consistencies:
func, opts = consistency
for opt in opts:
_consistencies.setdefault(opt, []).append((func, opts))
else:
2013-04-04 11:24:00 +02:00
_currpath.append(attr)
option.optimpl_build_cache(cache_path, cache_option, _currpath, _consistencies)
2013-04-04 11:24:00 +02:00
_currpath.pop()
2013-04-03 12:20:26 +02:00
if save:
#valid no duplicated option
valid_child = copy(cache_option)
valid_child.sort()
old = None
for child in valid_child:
if child == old:
raise ConflictError(_('duplicate option: '
'{0}').format(child))
2013-04-03 12:20:26 +02:00
old = child
self._cache_paths = (tuple(cache_option), tuple(cache_path))
self._consistencies = _consistencies
2013-04-03 12:20:26 +02:00
def optimpl_get_opt_by_path(self, path):
2013-04-03 12:20:26 +02:00
try:
return self._cache_paths[0][self._cache_paths[1].index(path)]
except ValueError:
2013-04-14 12:01:32 +02:00
raise AttributeError(_('no option for path {}').format(path))
2013-04-03 12:20:26 +02:00
def optimpl_get_path_by_opt(self, opt):
2013-04-03 12:20:26 +02:00
try:
return self._cache_paths[1][self._cache_paths[0].index(opt)]
except ValueError:
2013-04-14 12:01:32 +02:00
raise AttributeError(_('no option {} found').format(opt))
2013-04-03 12:20:26 +02:00
# ____________________________________________________________
def optimpl_set_group_type(self, group_type):
2012-12-06 18:14:57 +01:00
"""sets a given group object to an OptionDescription
2012-12-10 14:38:25 +01:00
:param group_type: an instance of `GroupType` or `MasterGroupType`
2012-12-06 18:14:57 +01:00
that lives in `setting.groups`
"""
2013-04-03 12:20:26 +02:00
if self._group_type != groups.default:
2013-04-14 12:01:32 +02:00
raise TypeError(_('cannot change group_type if already set '
'(old {}, new {})').format(self._group_type, group_type))
2012-12-10 14:38:25 +01:00
if isinstance(group_type, groups.GroupType):
2013-04-03 12:20:26 +02:00
self._group_type = group_type
2012-12-10 14:38:25 +01:00
if isinstance(group_type, groups.MasterGroupType):
2013-04-03 12:20:26 +02:00
#if master (same name has group) is set
identical_master_child_name = False
2013-04-03 12:20:26 +02:00
#for collect all slaves
slaves = []
master = None
for child in self.optimpl_getchildren():
if isinstance(child, OptionDescription):
2013-04-14 12:01:32 +02:00
raise ValueError(_("master group {} shall not have "
"a subgroup").format(self._name))
if not child.optimpl_is_multi():
2013-04-14 12:01:32 +02:00
raise ValueError(_("not allowed option {0} in group {1}"
": this option is not a multi"
"").format(child._name, self._name))
if child._name == self._name:
identical_master_child_name = True
child._multitype = multitypes.master
2013-04-03 12:20:26 +02:00
master = child
else:
slaves.append(child)
if master is None:
2013-04-14 12:01:32 +02:00
raise ValueError(_('master group with wrong master name for {}'
'').format(self._name))
master._master_slaves = tuple(slaves)
for child in self.optimpl_getchildren():
2013-04-03 12:20:26 +02:00
if child != master:
child._master_slaves = master
child._multitype = multitypes.slave
if not identical_master_child_name:
2013-04-14 12:01:32 +02:00
raise ValueError(_("the master group: {} has not any "
"master child").format(self._name))
else:
2013-04-14 12:01:32 +02:00
raise ValueError(_('not allowed group_type : {0}').format(group_type))
2012-10-05 16:00:07 +02:00
def optimpl_get_group_type(self):
2013-04-03 12:20:26 +02:00
return self._group_type
def _valid_consistency(self, opt, value, context, index):
2013-04-14 10:14:06 +02:00
consistencies = self._consistencies.get(opt)
if consistencies is not None:
for consistency in consistencies:
func, opts = consistency
ret = getattr(opts[0], func)(opt, value, context, index, opts)
if ret is False:
return False
return True
2012-09-20 10:51:35 +02:00
def validate_requires_arg(requires, name):
2013-04-03 12:20:26 +02:00
"check malformed requirements"
if requires is not None:
config_action = {}
for req in requires:
if not type(req) == tuple:
2013-04-14 12:01:32 +02:00
raise ValueError(_("malformed requirements type for option:"
" {0}, must be a tuple").format(name))
2013-04-03 12:20:26 +02:00
if len(req) == 3:
action = req[2]
inverse = False
elif len(req) == 4:
action = req[2]
inverse = req[3]
else:
2013-04-14 12:01:32 +02:00
raise ValueError(_("malformed requirements for option: {0}"
" invalid len").format(name))
2013-04-03 12:20:26 +02:00
if action in config_action:
if inverse != config_action[action]:
2013-04-14 12:01:32 +02:00
raise ValueError(_("inconsistency in action types for option: {0}"
" action: {1}").format(name, action))
2013-04-03 12:20:26 +02:00
else:
config_action[action] = inverse