tiramisu/tiramisu/option/masterslave.py

248 lines
12 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 tiramisu.i18n import _
from tiramisu.setting import log, undefined
from tiramisu.error import SlaveError, ConfigError
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:
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):
for slave in self.getslaves(opt):
values.reset(slave, validate=False)
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):
values._get_cached_item(slave, validate=False,
validate_properties=False
).pop(index, force=True)
pass
def getitem(self, values, opt, path, validate, force_permissive,
force_properties, validate_properties, slave_path=undefined,
slave_value=undefined):
if self.is_master(opt):
return self._getmaster(values, opt, path, validate,
force_permissive, force_properties,
validate_properties, slave_path,
slave_value)
else:
return self._getslave(values, opt, path, validate,
force_permissive, force_properties,
validate_properties)
def _getmaster(self, values, opt, path, validate, force_permissive,
force_properties, validate_properties, c_slave_path,
c_slave_value):
value = values._get_validated_value(opt, path, validate,
force_permissive,
force_properties,
validate_properties)
if validate is True:
masterlen = len(value)
for slave in self.getslaves(opt):
try:
slave_path = slave.impl_getpath(values._getcontext())
if c_slave_path == slave_path:
slave_value = c_slave_value
else:
slave_value = values._get_validated_value(slave,
slave_path,
False,
False,
None, False,
None) # not undefined
slavelen = len(slave_value)
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,
force_properties, validate_properties):
"""
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)
masterp = master.impl_getpath(values._getcontext())
masterlen = self.get_length(values, opt, validate, undefined,
undefined, force_permissive,
master=master)
master_is_meta = values._is_meta(opt, masterp)
value = values._get_validated_value(opt, path, validate,
force_permissive,
force_properties,
validate_properties,
None, # not undefined
with_meta=master_is_meta)
#if slave, had values until master's one
path = opt.impl_getpath(values._getcontext())
valuelen = len(value)
if validate:
self.validate_slave_length(masterlen, valuelen,
opt.impl_getname(), opt)
if valuelen < masterlen:
for num in range(0, masterlen - valuelen):
index = valuelen + num
value.append(values._get_validated_value(opt, path, True,
False, None,
validate_properties,
with_meta=master_is_meta,
index=index),
setitem=False,
force=True)
return value
def setitem(self, values, opt, value, path):
if self.is_master(opt):
masterlen = len(value)
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
slave_value = values._get_validated_value(slave,
slave_path,
False,
False,
None, False,
None) # not undefined
slavelen = len(slave_value)
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()))