tiramisu/tiramisu/option/masterslave.py

257 lines
13 KiB
Python

# -*- coding: utf-8 -*-
"master slave support"
# Copyright (C) 2014 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 log, undefined
from ..error import SlaveError, ConfigError, PropertiesOptionError
from .baseoption import DynSymLinkOption, SymLinkOption, Option
class MasterSlaves(object):
__slots__ = ('master', 'slaves')
def __init__(self, name, childs, validate=True):
#if master (same name has group) is set
#for collect all slaves
self.master = None
slaves = []
for child in childs:
if isinstance(child, SymLinkOption): # pragma: optional cover
raise ValueError(_("master group {0} shall not have "
"a symlinkoption").format(name))
if not isinstance(child, Option): # pragma: optional cover
raise ValueError(_("master group {0} shall not have "
"a subgroup").format(name))
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(), name))
if child.impl_getname() == name:
self.master = child
else:
if child.impl_getdefault() != []:
raise ValueError(_("not allowed default value for option {0} "
"in group {1}").format(child.impl_getname(),
name))
slaves.append(child)
if self.master is None: # pragma: optional cover
raise ValueError(_('master group with wrong'
' master name for {0}'
).format(name))
if validate:
callback, callback_params = self.master.impl_get_callback()
if callback is not None and callback_params != {}: # pragma: optional cover
for key, callbacks in callback_params.items():
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
self.slaves = tuple(slaves)
for child in childs:
child._master_slaves = self
def is_master(self, opt):
return opt == self.master or (isinstance(opt, DynSymLinkOption) and
opt._opt == self.master)
def getmaster(self, opt):
if isinstance(opt, DynSymLinkOption):
suffix = opt.impl_getsuffix()
name = self.master.impl_getname() + suffix
base_path = opt._dyn.split('.')[0] + '.'
path = base_path + name
master = self.master._impl_to_dyn(name, path)
else: # pragma: no dynoptiondescription cover
master = self.master
return master
def getslaves(self, opt):
if isinstance(opt, DynSymLinkOption):
for slave in self.slaves:
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: # pragma: no dynoptiondescription cover
for slave in self.slaves:
yield slave
def in_same_group(self, opt):
if isinstance(opt, DynSymLinkOption):
return opt._opt == self.master or opt._opt in self.slaves
else: # pragma: no dynoptiondescription cover
return opt == self.master or opt in self.slaves
def reset(self, opt, values, setting_properties):
for slave in self.getslaves(opt):
values.reset(slave, validate=False, _setting_properties=setting_properties)
def pop(self, opt, values, index):
for slave in self.getslaves(opt):
if not values.is_default_owner(slave, validate_properties=False,
validate_meta=False, index=index):
values._get_cached_value(slave, validate=False,
validate_properties=False
).pop(index, force=True)
pass
def getitem(self, values, opt, path, validate, force_permissive,
trusted_cached_properties, validate_properties, slave_path=undefined,
slave_value=undefined, setting_properties=undefined,
self_properties=undefined, index=None):
if self.is_master(opt):
return self._getmaster(values, opt, path, validate,
force_permissive,
validate_properties, slave_path,
slave_value, self_properties, index)
else:
return self._getslave(values, opt, path, validate,
force_permissive, trusted_cached_properties,
validate_properties, setting_properties,
self_properties, index)
def _getmaster(self, values, opt, path, validate, force_permissive,
validate_properties, c_slave_path,
c_slave_value, self_properties, index):
value = values._get_cached_value(opt, path=path, validate=validate,
force_permissive=force_permissive,
validate_properties=validate_properties,
self_properties=self_properties,
from_masterslave=True, index=index)
if index is None and validate is True:
masterlen = len(value)
for slave in self.getslaves(opt):
try:
slave_path = slave.impl_getpath(values._getcontext())
slavelen = values._p_.get_max_length(slave_path)
self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
except ConfigError: # pragma: optional cover
pass
return value
def _getslave(self, values, opt, path, validate, force_permissive,
trusted_cached_properties, validate_properties, setting_properties,
self_properties, index):
"""
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)
masterlen = self.get_length(values, opt, validate, undefined,
undefined, force_permissive,
master=master)
master_is_meta = values._is_meta(master, masterp)
multi = values._get_multi(opt, path)
if masterlen == 0:
if validate_properties:
context.cfgimpl_get_settings().validate_properties(opt, False,
False,
value=multi,
path=path,
force_permissive=force_permissive,
setting_properties=setting_properties)
else:
one_has_value = False
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,
with_meta=master_is_meta,
index=idx,
# not self_properties,
# depends to index
#self_properties=self_properties,
masterlen=masterlen,
from_masterslave=True)
multi.append(value, setitem=False, force=True, validate=validate)
one_has_value = True
except PropertiesOptionError, err:
multi.append_properties_error(err)
if not one_has_value:
#raise last err
raise err
return multi
def setitem(self, values, opt, value, path):
if self.is_master(opt):
masterlen = len(value)
#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(masterlen, slavelen, slave.impl_getname(), opt)
else:
self.validate_slave_length(self.get_length(values, opt,
slave_path=path), len(value),
opt.impl_getname(), opt, setitem=True)
def get_length(self, values, opt, validate=True, slave_path=undefined,
slave_value=undefined, force_permissive=False, master=None,
masterp=None):
"""get master len with slave option"""
if master is None:
master = self.getmaster(opt)
if masterp is None:
masterp = master.impl_getpath(values._getcontext())
if slave_value is undefined:
slave_path = undefined
return len(self.getitem(values, master, masterp, validate,
force_permissive, None, True, slave_path,
slave_value))
def validate_slave_length(self, masterlen, valuelen, name, opt, setitem=False):
if valuelen > masterlen or (valuelen < masterlen and setitem): # pragma: optional cover
log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, '
'setitem: {2}'.format(masterlen, valuelen, setitem))
raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format(
name, self.getmaster(opt).impl_getname()))