split optiondescription into masterslave, dynoptiondescription and syndynoptiondescription

This commit is contained in:
Emmanuel Garette 2017-11-29 23:48:37 +01:00
parent 911af90564
commit 3820f9fe4b
6 changed files with 606 additions and 555 deletions

1
.gitignore vendored
View file

@ -2,5 +2,6 @@
*# *#
*.pyc *.pyc
*.mo *.mo
*.swp
version.in version.in
build/ build/

View file

@ -1,5 +1,7 @@
from .optiondescription import OptionDescription, DynOptionDescription, \ from .optiondescription import OptionDescription
SynDynOptionDescription, MasterSlaves from .dynoptiondescription import DynOptionDescription
from .syndynoptiondescription import SynDynOptionDescription
from .masterslave import MasterSlaves
from .baseoption import SymLinkOption, DynSymLinkOption, submulti from .baseoption import SymLinkOption, DynSymLinkOption, submulti
from .option import Option from .option import Option
from .choiceoption import ChoiceOption from .choiceoption import ChoiceOption

View file

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 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 Lesser General Public License as published by the
# Free Software Foundation, either version 3 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# 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
# ____________________________________________________________
import re
from ..i18n import _
from .optiondescription import OptionDescription
from ..setting import groups, undefined
from ..error import ConfigError
from ..autolib import carry_out_calculation
NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
class DynOptionDescription(OptionDescription):
def __init__(self,
name,
doc,
children,
requires=None,
properties=None,
callback=None,
callback_params=None):
super(DynOptionDescription, self).__init__(name,
doc,
children,
requires,
properties)
# check children + set relation to this dynoptiondescription
for child in children:
if isinstance(child, OptionDescription):
if child.impl_get_group_type() != groups.master:
raise ConfigError(_('cannot set optiondescription in a '
'dynoptiondescription'))
for chld in child.impl_getchildren(setting_properties=undefined):
chld._impl_setsubdyn(self)
if child.impl_is_symlinkoption():
raise ConfigError(_('cannot set symlinkoption in a '
'dynoptiondescription'))
child._impl_setsubdyn(self)
# add callback
self.impl_set_callback(callback,
callback_params)
def _validate_callback(self,
callback,
callback_params):
if callback is None:
raise ConfigError(_('callback is mandatory for the dynoptiondescription "{}"'
'').format(self.impl_get_display_name()))
def _impl_get_suffixes(self,
context,
setting_properties):
callback, callback_params = self.impl_get_callback()
values = carry_out_calculation(self,
context=context,
callback=callback,
callback_params=callback_params,
setting_properties=setting_properties)
if not isinstance(values, list):
raise ValueError(_('invalid suffix "{}" for option "{}", must be a list'
'').format(values,
self.impl_get_display_name()))
if len(values) > len(set(values)):
raise ValueError(_('DynOptionDescription callback return not unique value'))
for val in values:
if not isinstance(val, str) or re.match(NAME_REGEXP, val) is None:
raise ValueError(_('invalid suffix "{}" for option "{}"'
'').format(val,
self.impl_get_display_name()))
return values

View file

@ -19,6 +19,430 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from .optiondescription import MasterSlaves import weakref
from ..i18n import _
from ..setting import groups, undefined, log, debug
from .optiondescription import OptionDescription
from .option import Option
from ..error import SlaveError, PropertiesOptionError
class MasterSlaves(OptionDescription):
__slots__ = ('master', 'slaves')
def __init__(self,
name,
doc,
children,
requires=None,
properties=None):
super(MasterSlaves, self).__init__(name,
doc,
children,
requires=requires,
properties=properties)
self._group_type = groups.master
slaves = []
master = children[0]
if not children:
raise ValueError(_('children is mandatory in masterslaves "{}"').format(name))
for child in children[1:]:
if child.impl_getdefault() != []:
raise ValueError(_("not allowed default value for option {0} "
"in master/slave object {1}").format(child.impl_getname(),
name))
slaves.append(child)
child._add_dependency(self)
for idx, child in enumerate(children):
if child.impl_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
raise ValueError(_("master group {0} shall not have "
"a subgroup").format(self.impl_getname()))
if not child.impl_is_multi(): # pragma: optional cover
raise ValueError(_("not allowed option {0} "
"in group {1}"
": this option is not a multi"
"").format(child.impl_getname(), self.impl_getname()))
# no empty property for save
if idx != 0:
properties = list(child._properties)
properties.remove('empty')
child._properties = tuple(properties)
callback, callback_params = master.impl_get_callback()
if callback is not None and callback_params != {}:
for callbacks in callback_params.values():
for callbk in callbacks:
if isinstance(callbk, tuple):
if callbk[0] in slaves:
raise ValueError(_("callback of master's option shall "
"not refered a slave's ones"))
#everything is ok, store references
for child in children:
child._master_slaves = weakref.ref(self)
master._add_dependency(self)
def is_master(self, opt):
master = self._children[0][0]
return opt.impl_getname() == master or (opt.impl_is_dynsymlinkoption() and
opt._opt.impl_getname() == master)
def getmaster(self, opt):
master = self._children[1][0]
if opt is not None and opt.impl_is_dynsymlinkoption():
suffix = opt.impl_getsuffix()
name = master.impl_getname() + suffix
base_path = opt._dyn.split('.')[0] + '.'
path = base_path + name
master = master._impl_to_dyn(name, path)
return master
def getslaves(self, opt):
if opt.impl_is_dynsymlinkoption():
for slave in self._children[1][1:]:
suffix = opt.impl_getsuffix()
name = slave.impl_getname() + suffix
base_path = opt._dyn.split('.')[0] + '.'
path = base_path + name
yield slave._impl_to_dyn(name, path)
else:
for slave in self._children[1][1:]:
yield slave
def in_same_group(self, opt):
if opt.impl_is_dynsymlinkoption():
c_opt = opt._opt
else:
c_opt = opt
return c_opt in self._children[1]
def reset(self,
opt,
values,
setting_properties,
_commit=True,
force_permissive=False):
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
values.reset(slave,
slave_path,
setting_properties,
validate=False,
_commit=_commit,
force_permissive=force_permissive)
def pop(self,
opt,
path,
values,
index,
setting_properties,
force_permissive=False):
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
slavelen = values._p_.get_max_length(slave_path)
if not values.is_default_owner(slave,
slave_path,
setting_properties,
validate_meta=False,
index=index,
force_permissive=force_permissive):
#FIXME # just for raise if needed
#multi = values.get_cached_value(slave,
# validate=False,
# validate_properties=False,
# )
#if isinstance(multi, Exception):
# raise multi
if slavelen > index:
values._p_.resetvalue_index(slave_path,
index)
if slavelen > index + 1:
for idx in range(index + 1, slavelen):
values._p_.reduce_index(slave_path,
idx)
def getitem(self,
values,
opt,
path,
validate,
force_permissive,
trusted_cached_properties,
validate_properties,
setting_properties=undefined,
self_properties=undefined,
index=None,
check_frozen=False):
if self.is_master(opt):
return self._getmaster(values,
opt,
path,
validate,
force_permissive,
validate_properties,
self_properties,
index,
setting_properties,
check_frozen)
else:
return self._getslave(values,
opt,
path,
validate,
force_permissive,
trusted_cached_properties,
validate_properties,
setting_properties,
self_properties,
index,
check_frozen)
def _getmaster(self,
values,
opt,
path,
validate,
force_permissive,
validate_properties,
self_properties,
index,
setting_properties,
check_frozen):
return values.get_cached_value(opt,
path=path,
validate=validate,
force_permissive=force_permissive,
self_properties=self_properties,
index=index,
setting_properties=setting_properties)
def _getslave(self, values, opt, path, validate, force_permissive,
trusted_cached_properties, validate_properties, setting_properties,
self_properties, index, check_frozen):
"""
if master has length 0:
return []
if master has length bigger than 0:
if default owner:
if has callback:
if return a list:
list same length as master: return list
list is smaller than master: return list + None
list is greater than master: raise SlaveError
if has default value:
list same length as master: return list
list is smaller than master: return list + None
list is greater than master: raise SlaveError
if has default_multi value:
return default_multi * master's length
if has value:
list same length as master: return list
list is smaller than master: return list + None
list is greater than master: raise SlaveError
"""
master = self.getmaster(opt)
context = values._getcontext()
masterp = master.impl_getpath(context)
try:
mastervalue = values.get_cached_value(master,
path=masterp,
validate=validate,
force_permissive=force_permissive,
validate_properties=validate_properties,
self_properties=self_properties,
from_masterslave=True,
setting_properties=setting_properties,
check_frozen=check_frozen)
except PropertiesOptionError as mastervalue:
mastervalue.set_orig_opt(opt)
raise mastervalue
masterlen = len(mastervalue)
#self._master_is_meta = values._is_meta(master, masterp, force_permissive=force_permissive)
multi = list() # values._get_multi(opt, path)
if validate_properties:
props = context.cfgimpl_get_settings().validate_properties(opt, False,
check_frozen,
value=multi,
path=path,
force_permissive=force_permissive,
setting_properties=setting_properties)
if props:
return props
#FIXME shouldn't have index!!!
if index is None:
indexes = range(0, masterlen)
else:
indexes = [index]
for idx in indexes:
try:
value = values.get_cached_value(opt,
path,
validate,
force_permissive,
trusted_cached_properties,
validate_properties,
index=idx,
# not self_properties,
# depends to index
#self_properties=self_properties,
setting_properties=setting_properties,
from_masterslave=True,
check_frozen=check_frozen)
except PropertiesOptionError as perr:
err = perr
if index is None:
multi.append(err)
else:
multi = err
if index is None:
multi.append(value)
else:
multi = value
return multi
def validate(self,
values,
opt,
index,
path,
setitem):
if self.is_master(opt):
#for regen slave path
base_path = '.'.join(path.split('.')[:-1]) + '.'
for slave in self.getslaves(opt):
slave_path = base_path + slave.impl_getname()
slavelen = values._p_.get_max_length(slave_path)
self.validate_slave_length(index,
slavelen,
slave.impl_getname(),
opt)
else:
val_len = self.get_length(values)
if isinstance(val_len, Exception):
return val_len
self.validate_slave_length(val_len,
index,
opt.impl_getname(),
opt,
setitem=setitem)
def get_length(self,
values,
validate=True,
force_permissive=False,
master=None,
masterp=None,
setting_properties=undefined):
"""get master len with slave option"""
if master is None:
master = self.getmaster(None)
if masterp is None:
masterp = master.impl_getpath(values._getcontext())
value = self._getmaster(values,
master,
masterp,
validate,
force_permissive,
validate,
undefined,
None,
setting_properties,
False)
if isinstance(value, Exception):
return value
return len(value)
def validate_slave_length(self,
masterlen,
valuelen,
name,
opt,
setitem=False):
if valuelen > masterlen or (valuelen < masterlen and setitem):
if debug: # pragma: no cover
log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, '
'setitem: {2}'.format(masterlen, valuelen, setitem))
if not opt.impl_is_master_slaves('master'):
opt = self.getmaster(opt)
raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format(
name, opt.impl_getname()))
def reset_cache(self,
opt,
path,
obj,
type_,
resetted_opts):
context = obj._getcontext()
#FIXME pb avec dyn, devrait etre une option
mopt = self.getmaster(None)
mpath = mopt.impl_getpath(context)
mopt.reset_cache(mopt,
mpath,
obj,
type_,
resetted_opts)
for slave in self.getslaves(mopt):
spath = slave.impl_getpath(context)
slave.reset_cache(slave,
spath,
obj,
type_,
resetted_opts)
def impl_getchild(self,
name,
setting_properties,
context=undefined,
dyn=True):
return super(MasterSlaves, self).impl_getchild(name,
setting_properties,
context,
dyn)
def impl_validate(self,
context,
force_permissive,
setting_properties,
masterlen=None,
slavelen=None,
opt=None,
setitem=False):
values = context.cfgimpl_get_values()
if masterlen is None:
master = self.getmaster(opt)
masterp = master.impl_getpath(context)
mastervalue = values.get_cached_value(master, path=masterp,
force_permissive=force_permissive,
setting_properties=setting_properties)
if isinstance(mastervalue, Exception):
return mastervalue
masterlen = len(mastervalue)
else:
master = opt
if slavelen is not None:
self.validate_slave_length(masterlen, slavelen, opt.impl_getname(), master, setitem=setitem)
else:
for slave in self.getslaves(master):
slave_path = slave.impl_getpath(context)
slavelen = values._p_.get_max_length(slave_path)
self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), master)
def impl_validate_value(self,
option,
value,
context):
if option.impl_is_master_slaves('master') and isinstance(value, list):
if len(value) < context._impl_length:
return ValueError(_('cannot reduce length of master "{}"'
'').format(option.impl_get_display_name()))

View file

@ -19,25 +19,14 @@
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from copy import copy from copy import copy
import re
import weakref
from ..i18n import _ from ..i18n import _
from ..setting import groups, undefined, owners, log, debug from ..setting import groups, undefined, owners
from .baseoption import BaseOption from .baseoption import BaseOption
from .option import Option, ALLOWED_CONST_LIST from .option import ALLOWED_CONST_LIST
#from . import MasterSlaves from .syndynoptiondescription import SynDynOptionDescription
from ..error import ConfigError, ConflictError, SlaveError, PropertiesOptionError from ..error import ConfigError, ConflictError
from ..autolib import carry_out_calculation
NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
import sys
if sys.version_info[0] >= 3: # pragma: no cover
xrange = range
del(sys)
class CacheOptionDescription(BaseOption): class CacheOptionDescription(BaseOption):
@ -138,7 +127,7 @@ class CacheOptionDescription(BaseOption):
require_opt.impl_getname(), option.impl_getname())) require_opt.impl_getname(), option.impl_getname()))
if init: if init:
if len(cache_option) != len(set(cache_option)): if len(cache_option) != len(set(cache_option)):
for idx in xrange(1, len(cache_option) + 1): for idx in range(1, len(cache_option) + 1):
opt = cache_option.pop(0) opt = cache_option.pop(0)
if opt in cache_option: if opt in cache_option:
raise ConflictError(_('duplicate option: {0}').format(opt)) raise ConflictError(_('duplicate option: {0}').format(opt))
@ -234,7 +223,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
dynopt): dynopt):
found = False found = False
spath = path.split('.') spath = path.split('.')
for length in xrange(1, len(spath)): for length in range(1, len(spath)):
subpath = '.'.join(spath[0:length]) subpath = '.'.join(spath[0:length])
subopt = self.impl_get_opt_by_path(subpath) subopt = self.impl_get_opt_by_path(subpath)
if dynopt == subopt: if dynopt == subopt:
@ -243,7 +232,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
if not found: # pragma: no cover if not found: # pragma: no cover
raise ConfigError(_('cannot find dynpath')) raise ConfigError(_('cannot find dynpath'))
subpath = subpath + suffix subpath = subpath + suffix
for slength in xrange(length, len(spath)): for slength in range(length, len(spath)):
subpath = subpath + '.' + spath[slength] + suffix subpath = subpath + '.' + spath[slength] + suffix
return subpath return subpath
@ -445,7 +434,7 @@ class OptionDescription(OptionDescriptionWalk):
for child in children: for child in children:
name = child.impl_getname() name = child.impl_getname()
child_names.append(name) child_names.append(name)
if isinstance(child, DynOptionDescription): if child.impl_is_dynoptiondescription():
dynopt_names.append(name) dynopt_names.append(name)
#better performance like this #better performance like this
@ -503,536 +492,3 @@ class OptionDescription(OptionDescriptionWalk):
value, value,
context): context):
pass pass
class DynOptionDescription(OptionDescription):
def __init__(self,
name,
doc,
children,
requires=None,
properties=None,
callback=None,
callback_params=None):
super(DynOptionDescription, self).__init__(name,
doc,
children,
requires,
properties)
# check children + set relation to this dynoptiondescription
for child in children:
if isinstance(child, OptionDescription):
if child.impl_get_group_type() != groups.master:
raise ConfigError(_('cannot set optiondescription in a '
'dynoptiondescription'))
for chld in child.impl_getchildren(setting_properties=undefined):
chld._impl_setsubdyn(self)
if child.impl_is_symlinkoption():
raise ConfigError(_('cannot set symlinkoption in a '
'dynoptiondescription'))
child._impl_setsubdyn(self)
# add callback
self.impl_set_callback(callback,
callback_params)
def _validate_callback(self,
callback,
callback_params):
if callback is None:
raise ConfigError(_('callback is mandatory for the dynoptiondescription "{}"'
'').format(self.impl_get_display_name()))
def _impl_get_suffixes(self,
context,
setting_properties):
callback, callback_params = self.impl_get_callback()
values = carry_out_calculation(self,
context=context,
callback=callback,
callback_params=callback_params,
setting_properties=setting_properties)
if not isinstance(values, list):
raise ValueError(_('invalid suffix "{}" for option "{}", must be a list'
'').format(values,
self.impl_get_display_name()))
if len(values) > len(set(values)):
raise ValueError(_('DynOptionDescription callback return not unique value'))
for val in values:
if not isinstance(val, str) or re.match(NAME_REGEXP, val) is None:
raise ValueError(_('invalid suffix "{}" for option "{}"'
'').format(val,
self.impl_get_display_name()))
return values
class SynDynOptionDescription(object):
__slots__ = ('_opt',
'_name',
'_suffix')
def __init__(self,
opt,
name,
suffix):
self._opt = opt
self._name = name
self._suffix = suffix
def __getattr__(self, name):
return getattr(self._opt, name)
def impl_getchild(self,
name,
setting_properties,
context):
if name.endswith(self._suffix):
oname = name[:-len(self._suffix)]
child = self._children[1][self._children[0].index(oname)]
return self._impl_get_dynchild(child,
self._suffix)
raise AttributeError(_('unknown Option {0} '
'in SynDynOptionDescription {1}'
'').format(name, self.impl_getname()))
def impl_getname(self):
return self._name
def impl_getchildren(self,
setting_properties,
dyn=True,
context=undefined):
children = []
for child in self._opt.impl_getchildren(setting_properties):
yield(self._opt._impl_get_dynchild(child,
self._suffix))
def impl_getpath(self, context):
path = self.impl_getopt().impl_getpath(context).split('.')
path[-1] += self._suffix
path.append(self._name)
return '.'.join(path)
def impl_getopt(self):
return self._opt
class MasterSlaves(OptionDescription):
__slots__ = ('master', 'slaves')
def __init__(self,
name,
doc,
children,
requires=None,
properties=None):
super(MasterSlaves, self).__init__(name,
doc,
children,
requires=requires,
properties=properties)
self._group_type = groups.master
slaves = []
master = children[0]
if not children:
raise ValueError(_('children is mandatory in masterslaves "{}"').format(name))
for child in children[1:]:
if child.impl_getdefault() != []:
raise ValueError(_("not allowed default value for option {0} "
"in master/slave object {1}").format(child.impl_getname(),
name))
slaves.append(child)
child._add_dependency(self)
for idx, child in enumerate(children):
if child.impl_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
raise ValueError(_("master group {0} shall not have "
"a subgroup").format(self.impl_getname()))
if not child.impl_is_multi(): # pragma: optional cover
raise ValueError(_("not allowed option {0} "
"in group {1}"
": this option is not a multi"
"").format(child.impl_getname(), self.impl_getname()))
# no empty property for save
if idx != 0:
properties = list(child._properties)
properties.remove('empty')
child._properties = tuple(properties)
callback, callback_params = master.impl_get_callback()
if callback is not None and callback_params != {}:
for callbacks in callback_params.values():
for callbk in callbacks:
if isinstance(callbk, tuple):
if callbk[0] in slaves:
raise ValueError(_("callback of master's option shall "
"not refered a slave's ones"))
#everything is ok, store references
for child in children:
child._master_slaves = weakref.ref(self)
master._add_dependency(self)
def is_master(self, opt):
master = self._children[0][0]
return opt.impl_getname() == master or (opt.impl_is_dynsymlinkoption() and
opt._opt.impl_getname() == master)
def getmaster(self, opt):
master = self._children[1][0]
if opt is not None and opt.impl_is_dynsymlinkoption():
suffix = opt.impl_getsuffix()
name = master.impl_getname() + suffix
base_path = opt._dyn.split('.')[0] + '.'
path = base_path + name
master = master._impl_to_dyn(name, path)
return master
def getslaves(self, opt):
if opt.impl_is_dynsymlinkoption():
for slave in self._children[1][1:]:
suffix = opt.impl_getsuffix()
name = slave.impl_getname() + suffix
base_path = opt._dyn.split('.')[0] + '.'
path = base_path + name
yield slave._impl_to_dyn(name, path)
else:
for slave in self._children[1][1:]:
yield slave
def in_same_group(self, opt):
if opt.impl_is_dynsymlinkoption():
c_opt = opt._opt
else:
c_opt = opt
return c_opt in self._children[1]
def reset(self,
opt,
values,
setting_properties,
_commit=True,
force_permissive=False):
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
values.reset(slave,
slave_path,
setting_properties,
validate=False,
_commit=_commit,
force_permissive=force_permissive)
def pop(self,
opt,
path,
values,
index,
setting_properties,
force_permissive=False):
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
slavelen = values._p_.get_max_length(slave_path)
if not values.is_default_owner(slave,
slave_path,
setting_properties,
validate_meta=False,
index=index,
force_permissive=force_permissive):
#FIXME # just for raise if needed
#multi = values.get_cached_value(slave,
# validate=False,
# validate_properties=False,
# )
#if isinstance(multi, Exception):
# raise multi
if slavelen > index:
values._p_.resetvalue_index(slave_path,
index)
if slavelen > index + 1:
for idx in xrange(index + 1, slavelen):
values._p_.reduce_index(slave_path,
idx)
def getitem(self,
values,
opt,
path,
validate,
force_permissive,
trusted_cached_properties,
validate_properties,
setting_properties=undefined,
self_properties=undefined,
index=None,
check_frozen=False):
if self.is_master(opt):
return self._getmaster(values,
opt,
path,
validate,
force_permissive,
validate_properties,
self_properties,
index,
setting_properties,
check_frozen)
else:
return self._getslave(values,
opt,
path,
validate,
force_permissive,
trusted_cached_properties,
validate_properties,
setting_properties,
self_properties,
index,
check_frozen)
def _getmaster(self,
values,
opt,
path,
validate,
force_permissive,
validate_properties,
self_properties,
index,
setting_properties,
check_frozen):
return values.get_cached_value(opt,
path=path,
validate=validate,
force_permissive=force_permissive,
self_properties=self_properties,
index=index,
setting_properties=setting_properties)
def _getslave(self, values, opt, path, validate, force_permissive,
trusted_cached_properties, validate_properties, setting_properties,
self_properties, index, check_frozen):
"""
if master has length 0:
return []
if master has length bigger than 0:
if default owner:
if has callback:
if return a list:
list same length as master: return list
list is smaller than master: return list + None
list is greater than master: raise SlaveError
if has default value:
list same length as master: return list
list is smaller than master: return list + None
list is greater than master: raise SlaveError
if has default_multi value:
return default_multi * master's length
if has value:
list same length as master: return list
list is smaller than master: return list + None
list is greater than master: raise SlaveError
"""
master = self.getmaster(opt)
context = values._getcontext()
masterp = master.impl_getpath(context)
try:
mastervalue = values.get_cached_value(master,
path=masterp,
validate=validate,
force_permissive=force_permissive,
validate_properties=validate_properties,
self_properties=self_properties,
from_masterslave=True,
setting_properties=setting_properties,
check_frozen=check_frozen)
except PropertiesOptionError as mastervalue:
mastervalue.set_orig_opt(opt)
raise mastervalue
masterlen = len(mastervalue)
#self._master_is_meta = values._is_meta(master, masterp, force_permissive=force_permissive)
multi = list() # values._get_multi(opt, path)
if validate_properties:
props = context.cfgimpl_get_settings().validate_properties(opt, False,
check_frozen,
value=multi,
path=path,
force_permissive=force_permissive,
setting_properties=setting_properties)
if props:
return props
#FIXME shouldn't have index!!!
if index is None:
indexes = xrange(0, masterlen)
else:
indexes = [index]
for idx in indexes:
try:
value = values.get_cached_value(opt,
path,
validate,
force_permissive,
trusted_cached_properties,
validate_properties,
index=idx,
# not self_properties,
# depends to index
#self_properties=self_properties,
setting_properties=setting_properties,
from_masterslave=True,
check_frozen=check_frozen)
except PropertiesOptionError as perr:
err = perr
if index is None:
multi.append(err)
else:
multi = err
if index is None:
multi.append(value)
else:
multi = value
return multi
def validate(self,
values,
opt,
index,
path,
setitem):
if self.is_master(opt):
#for regen slave path
base_path = '.'.join(path.split('.')[:-1]) + '.'
for slave in self.getslaves(opt):
slave_path = base_path + slave.impl_getname()
slavelen = values._p_.get_max_length(slave_path)
self.validate_slave_length(index,
slavelen,
slave.impl_getname(),
opt)
else:
val_len = self.get_length(values)
if isinstance(val_len, Exception):
return val_len
self.validate_slave_length(val_len,
index,
opt.impl_getname(),
opt,
setitem=setitem)
def get_length(self,
values,
validate=True,
force_permissive=False,
master=None,
masterp=None,
setting_properties=undefined):
"""get master len with slave option"""
if master is None:
master = self.getmaster(None)
if masterp is None:
masterp = master.impl_getpath(values._getcontext())
value = self._getmaster(values,
master,
masterp,
validate,
force_permissive,
validate,
undefined,
None,
setting_properties,
False)
if isinstance(value, Exception):
return value
return len(value)
def validate_slave_length(self,
masterlen,
valuelen,
name,
opt,
setitem=False):
if valuelen > masterlen or (valuelen < masterlen and setitem):
if debug: # pragma: no cover
log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, '
'setitem: {2}'.format(masterlen, valuelen, setitem))
if not opt.impl_is_master_slaves('master'):
opt = self.getmaster(opt)
raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format(
name, opt.impl_getname()))
def reset_cache(self,
opt,
path,
obj,
type_,
resetted_opts):
context = obj._getcontext()
#FIXME pb avec dyn, devrait etre une option
mopt = self.getmaster(None)
mpath = mopt.impl_getpath(context)
mopt.reset_cache(mopt,
mpath,
obj,
type_,
resetted_opts)
for slave in self.getslaves(mopt):
spath = slave.impl_getpath(context)
slave.reset_cache(slave,
spath,
obj,
type_,
resetted_opts)
def impl_getchild(self,
name,
setting_properties,
context=undefined,
dyn=True):
return super(MasterSlaves, self).impl_getchild(name,
setting_properties,
context,
dyn)
def impl_validate(self,
context,
force_permissive,
setting_properties,
masterlen=None,
slavelen=None,
opt=None,
setitem=False):
values = context.cfgimpl_get_values()
if masterlen is None:
master = self.getmaster(opt)
masterp = master.impl_getpath(context)
mastervalue = values.get_cached_value(master, path=masterp,
force_permissive=force_permissive,
setting_properties=setting_properties)
if isinstance(mastervalue, Exception):
return mastervalue
masterlen = len(mastervalue)
else:
master = opt
if slavelen is not None:
self.validate_slave_length(masterlen, slavelen, opt.impl_getname(), master, setitem=setitem)
else:
for slave in self.getslaves(master):
slave_path = slave.impl_getpath(context)
slavelen = values._p_.get_max_length(slave_path)
self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), master)
def impl_validate_value(self,
option,
value,
context):
if option.impl_is_master_slaves('master') and isinstance(value, list):
if len(value) < context._impl_length:
return ValueError(_('cannot reduce length of master "{}"'
'').format(option.impl_get_display_name()))

View file

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 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 Lesser General Public License as published by the
# Free Software Foundation, either version 3 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# 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
# ____________________________________________________________
from ..i18n import _
from ..setting import undefined
class SynDynOptionDescription(object):
__slots__ = ('_opt',
'_name',
'_suffix')
def __init__(self,
opt,
name,
suffix):
self._opt = opt
self._name = name
self._suffix = suffix
def __getattr__(self, name):
return getattr(self._opt, name)
def impl_getchild(self,
name,
setting_properties,
context):
if name.endswith(self._suffix):
oname = name[:-len(self._suffix)]
child = self._children[1][self._children[0].index(oname)]
return self._impl_get_dynchild(child,
self._suffix)
raise AttributeError(_('unknown Option {0} '
'in SynDynOptionDescription {1}'
'').format(name, self.impl_getname()))
def impl_getname(self):
return self._name
def impl_getchildren(self,
setting_properties,
dyn=True,
context=undefined):
children = []
for child in self._opt.impl_getchildren(setting_properties):
yield(self._opt._impl_get_dynchild(child,
self._suffix))
def impl_getpath(self, context):
path = self.impl_getopt().impl_getpath(context).split('.')
path[-1] += self._suffix
path.append(self._name)
return '.'.join(path)
def impl_getopt(self):
return self._opt