tiramisu/tiramisu/option/masterslave.py

277 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, debug
from ..error import SlaveError, PropertiesOptionError
from ..storage import get_storages_option
StorageMasterSlaves = get_storages_option('masterslaves')
class MasterSlaves(object):
__slots__ = ('_p_')
def __init__(self, name, childs=None, validate=True, add=True):
#if master (same name has group) is set
#for collect all slaves
if isinstance(name, StorageMasterSlaves):
self._p_ = name
else:
slaves = []
if childs[0].impl_getname() == name:
master = childs[0]
else:
raise ValueError(_('master group with wrong'
' master name for {0}'
).format(name))
for child in childs[1:]:
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 validate:
callback, callback_params = 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._p_ = StorageMasterSlaves(master, slaves)
if add:
for child in childs:
child._set_master_slaves(self)
def is_master(self, opt):
master = self._p_._sm_getmaster().impl_getname()
return opt.impl_getname() == master or (opt.impl_is_dynsymlinkoption() and
opt._opt.impl_getname() == master)
def getmaster(self, opt):
master = self._p_._sm_getmaster()
if 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._p_._sm_getslaves():
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._p_._sm_getslaves():
yield slave
def in_same_group(self, opt):
if opt.impl_is_dynsymlinkoption():
return opt._opt == self._p_._sm_getmaster() or opt._opt in self._p_._sm_getslaves()
else: # pragma: no dynoptiondescription cover
return opt == self._p_._sm_getmaster() or opt in self._p_._sm_getslaves()
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):
multi = values._get_cached_value(slave, validate=False,
validate_properties=False,
)
if isinstance(multi, Exception):
raise multi
multi.pop(index, force=True)
def getitem(self, values, opt, path, validate, force_permissive,
trusted_cached_properties, validate_properties, session,
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,
setting_properties, session)
else:
return self._getslave(values, opt, path, validate,
force_permissive, trusted_cached_properties,
validate_properties, setting_properties,
self_properties, index,
session)
def _getmaster(self, values, opt, path, validate, force_permissive,
validate_properties, c_slave_path,
c_slave_value, self_properties, index,
setting_properties, session):
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,
setting_properties=setting_properties)
if isinstance(value, Exception):
return value
if index is None and validate is True:
masterlen = len(value)
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
slavelen = values._p_.get_max_length(slave_path, session)
self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
return value
def _getslave(self, values, opt, path, validate, force_permissive,
trusted_cached_properties, validate_properties, setting_properties,
self_properties, index, session):
"""
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, session, validate, undefined,
undefined, force_permissive,
master=master)
if isinstance(masterlen, Exception):
return masterlen
master_is_meta = values._is_meta(master, masterp, session)
multi = values._get_multi(opt, path)
#if masterlen is [], test properties (has no value, don't get any value)
if masterlen == 0:
if validate_properties:
props = context.cfgimpl_get_settings().validate_properties(opt, False,
False,
value=multi,
path=path,
force_permissive=force_permissive,
setting_properties=setting_properties)
if props:
return props
else:
one_has_value = False
if index is None:
indexes = range(0, masterlen)
else:
indexes = [index]
for idx in indexes:
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,
setting_properties=setting_properties,
masterlen=masterlen,
from_masterslave=True)
if isinstance(value, Exception):
if isinstance(value, PropertiesOptionError):
err = value
multi.append_properties_error(value)
else:
return value
elif index is None:
multi.append(value, setitem=False, force=True, validate=validate,
force_permissive=force_permissive)
one_has_value = True
else:
multi = value
one_has_value = True
if not one_has_value:
#raise last err
return err
return multi
def validate(self, values, opt, value, path, session):
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, session)
self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
else:
val_len = self.get_length(values, opt, session, slave_path=path)
if isinstance(val_len, Exception):
return val_len
self.validate_slave_length(val_len,
len(value),
opt.impl_getname(), opt, setitem=True)
def get_length(self, values, opt, session, 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
value = self.getitem(values, master, masterp, validate,
force_permissive, None, True, session, slave_path=slave_path,
slave_value=slave_value)
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): # pragma: optional cover
if debug:
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()))