# -*- 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 . # # 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, 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, returns_raise=False): 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, returns_raise) 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): 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) return value def _getslave(self, values, opt, path, validate, force_permissive, trusted_cached_properties, validate_properties, setting_properties, self_properties, index, returns_raise): """ 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 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: if returns_raise: return props else: raise 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, masterlen=masterlen, from_masterslave=True, returns_raise=True) if isinstance(value, PropertiesOptionError): err = value multi.append_properties_error(value) elif isinstance(value, Exception): raise value else: multi.append(value, setitem=False, force=True, validate=validate) one_has_value = True if not one_has_value: #raise last err raise err return multi def validate(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()))