tiramisu/tiramisu/option/masterslaves.py

227 lines
9.9 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 typing import List, Iterator, Optional, Any
from ..i18n import _
from ..setting import groups, undefined, OptionBag, Settings
from ..value import Values
from .optiondescription import OptionDescription
from .syndynoptiondescription import SynDynMasterSlaves
from .baseoption import BaseOption
from .option import Option
from ..error import SlaveError, PropertiesOptionError, RequirementError
from ..function import ParamOption
class MasterSlaves(OptionDescription):
__slots__ = ('master',
'slaves')
def __init__(self,
name: str,
doc: str,
children: List[BaseOption],
requires=None,
properties=None) -> 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 __debug__:
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()))
if idx != 0:
# remove empty property for slave
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 is not None:
for callbk in chain(callback_params.args, callback_params.kwargs.values()):
if isinstance(callbk, ParamOption) and callbk.option in slaves:
raise ValueError(_("callback of master's option shall "
"not refered to a slave's ones"))
# master should not have requires, only MasterSlaves should have
# so move requires to MasterSlaves
# if MasterSlaves has requires too, cannot manage this move so raises
master_requires = getattr(master, '_requires', None)
if master_requires:
if __debug__ and 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 __debug__ and 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')
if __debug__:
for requires_ in getattr(self, '_requires', ()):
for require in requires_:
for require_opt, values in require[0]:
if require_opt.impl_is_multi() and 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: Option) -> bool:
master = self.getmaster()
return opt == master or (opt.impl_is_dynsymlinkoption() and opt.opt == master)
def getmaster(self) -> Option:
return self._children[1][0]
def getslaves(self) -> Iterator[Option]:
for slave in self._children[1][1:]:
yield slave
def in_same_group(self,
opt: Option) -> bool:
if opt.impl_is_dynsymlinkoption():
opt = opt.opt
return opt in self._children[1]
def reset(self,
values: Values,
option_bag: OptionBag,
_commit: bool=True) -> None:
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: Values,
index: int,
option_bag: OptionBag,
slaves: Optional[List[Option]]=undefined) -> None:
if slaves is undefined:
# slaves is not undefined only in SynDynMasterSlaves
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) and 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: str,
values: Values,
settings: Settings,
resetted_opts: List[Option]) -> None:
self._reset_cache(path,
self.getmaster(),
self.getslaves(),
values,
settings,
resetted_opts)
def _reset_cache(self,
path: str,
master: Option,
slaves: List[Option],
values: Values,
settings: Settings,
resetted_opts: List[Option]) -> None:
super().reset_cache(path,
values,
settings,
resetted_opts)
master.reset_cache(master.impl_getpath(),
values,
settings,
None)
for slave in slaves:
spath = slave.impl_getpath()
slave.reset_cache(spath,
values,
settings,
None)
resetted_opts.append(spath)
def impl_is_master_slaves(self) -> None:
return True
def to_dynoption(self,
rootpath: str,
suffix: str) -> SynDynMasterSlaves:
return SynDynMasterSlaves(self,
rootpath,
suffix)