tiramisu/tiramisu/option/masterslaves.py

228 lines
9.6 KiB
Python

# -*- coding: utf-8 -*-
"master slaves support"
# Copyright (C) 2014-2018 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 weakref
from itertools import chain
from ..i18n import _
from ..setting import groups, undefined, OptionBag
from .optiondescription import OptionDescription
from .option import Option
from ..error import SlaveError, PropertiesOptionError, RequirementError
from ..function import ParamOption
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 = []
if len(children) < 2:
raise ValueError(_('a master and a slave are mandatories in masterslaves "{}"').format(name))
master = children[0]
for idx, child in enumerate(children):
if child.impl_is_symlinkoption():
raise ValueError(_('masterslaves "{0}" shall not have '
"a symlinkoption").format(self.impl_get_display_name()))
if not isinstance(child, Option):
raise ValueError(_('masterslaves "{0}" shall not have '
'a subgroup').format(self.impl_get_display_name()))
if not child.impl_is_multi():
raise ValueError(_('only multi option allowed in masterslaves "{0}" but option '
'"{1}" is not a multi').format(self.impl_get_display_name(),
child.impl_get_display_name()))
if idx != 0 and child.impl_getdefault() != []:
raise ValueError(_('not allowed default value for option "{0}" '
'in masterslaves "{1}"'
'').format(child.impl_get_display_name(),
self.impl_get_display_name()))
# no empty property for save
if idx != 0:
child_properties = list(child._properties)
child_properties.remove('empty')
child._properties = frozenset(child_properties)
slaves.append(child)
child._add_dependency(self)
child._master_slaves = weakref.ref(self)
callback, callback_params = master.impl_get_callback()
if callback is not None and callback_params != None:
for callbk in chain(callback_params.args, callback_params.kwargs.values()):
if isinstance(callbk, ParamOption):
if callbk.option in slaves:
raise ValueError(_("callback of master's option shall "
"not refered a slave's ones"))
# master should not have requires, only MasterSlaves should have
# so move requires to MasterSlaves
# if MasterSlaves has requires to, cannot manage the move so raises
master_requires = getattr(master, '_requires', None)
if master_requires:
if self.impl_getrequires():
raise RequirementError(_('master {} have requirement, but MasterSlaves {} too'
'').format(master.impl_getname(),
self.impl_getname()))
master_calproperties = getattr(master, '_calc_properties', None)
if master_calproperties:
if properties is not None:
self.validate_properties(name, master_calproperties, frozenset(properties))
setattr(self, '_calc_properties', master_calproperties)
setattr(self, '_requires', master_requires)
delattr(master, '_requires')
all_requires = getattr(self, '_requires', None)
if all_requires:
for requires_ in all_requires:
for require in requires_:
for require_opt, values in require[0]:
if require_opt.impl_is_multi():
if require_opt.impl_is_master_slaves():
raise ValueError(_('malformed requirements option "{0}" '
'must not be in slave for "{1}"').format(
require_opt.impl_getname(), self.impl_getname()))
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):
return self._children[1][0]
def getslaves(self):
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,
values,
option_bag,
_commit=True):
config_bag = option_bag.config_bag.copy()
config_bag.remove_validation()
for slave in self.getslaves():
slave_path = slave.impl_getpath()
soption_bag = OptionBag()
soption_bag.set_option(slave,
slave_path,
None,
config_bag)
values.reset(soption_bag,
_commit=_commit)
def pop(self,
values,
index,
option_bag,
slaves=undefined):
if slaves is undefined:
slaves = self.getslaves()
config_bag = option_bag.config_bag.copy()
config_bag.remove_validation()
for slave in slaves:
slave_path = slave.impl_getpath()
slavelen = values._p_.get_max_length(slave_path)
soption_bag = OptionBag()
soption_bag.set_option(slave,
slave_path,
index,
config_bag)
# do not check force_default_on_freeze
soption_bag.properties = set()
if not values.is_default_owner(soption_bag,
validate_meta=False):
if slavelen > index:
values._p_.resetvalue_index(slave_path,
index,
True)
if slavelen > index + 1:
for idx in range(index + 1, slavelen):
if values._p_.hasvalue(slave_path, idx):
values._p_.reduce_index(slave_path,
idx)
def reset_cache(self,
path,
values,
settings,
resetted_opts):
master = self.getmaster()
slaves = self.getslaves()
self._reset_cache(path,
master,
slaves,
values,
settings,
resetted_opts)
def _reset_cache(self,
path,
master,
slaves,
values,
settings,
resetted_opts):
super().reset_cache(path,
values,
settings,
resetted_opts)
mpath = master.impl_getpath()
master.reset_cache(mpath,
values,
settings,
None)
for slave in slaves:
spath = slave.impl_getpath()
slave.reset_cache(spath,
values,
settings,
None)
resetted_opts.append(spath)
def impl_validate_value(self,
option,
value,
context):
if isinstance(value, list) and len(value) < context._impl_length:
raise SlaveError(_('cannot reduce length of the master "{}"'
'').format(option.impl_get_display_name()))
def impl_is_master_slaves(self, *args, **kwargs):
return True