From 3820f9fe4be13de90e74c87e960e3c26b1f61f52 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 29 Nov 2017 23:48:37 +0100 Subject: [PATCH] split optiondescription into masterslave, dynoptiondescription and syndynoptiondescription --- .gitignore | 1 + tiramisu/option/__init__.py | 6 +- tiramisu/option/dynoptiondescription.py | 94 ++++ tiramisu/option/masterslave.py | 426 +++++++++++++++- tiramisu/option/optiondescription.py | 560 +-------------------- tiramisu/option/syndynoptiondescription.py | 74 +++ 6 files changed, 606 insertions(+), 555 deletions(-) create mode 100644 tiramisu/option/dynoptiondescription.py create mode 100644 tiramisu/option/syndynoptiondescription.py diff --git a/.gitignore b/.gitignore index d2b56ea..4802f20 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ *# *.pyc *.mo +*.swp version.in build/ diff --git a/tiramisu/option/__init__.py b/tiramisu/option/__init__.py index f78de2f..d770343 100644 --- a/tiramisu/option/__init__.py +++ b/tiramisu/option/__init__.py @@ -1,5 +1,7 @@ -from .optiondescription import OptionDescription, DynOptionDescription, \ - SynDynOptionDescription, MasterSlaves +from .optiondescription import OptionDescription +from .dynoptiondescription import DynOptionDescription +from .syndynoptiondescription import SynDynOptionDescription +from .masterslave import MasterSlaves from .baseoption import SymLinkOption, DynSymLinkOption, submulti from .option import Option from .choiceoption import ChoiceOption diff --git a/tiramisu/option/dynoptiondescription.py b/tiramisu/option/dynoptiondescription.py new file mode 100644 index 0000000..ac48574 --- /dev/null +++ b/tiramisu/option/dynoptiondescription.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 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 +# ____________________________________________________________ +import re + + +from ..i18n import _ +from .optiondescription import OptionDescription +from ..setting import groups, undefined +from ..error import ConfigError +from ..autolib import carry_out_calculation + + +NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$') + + +class DynOptionDescription(OptionDescription): + + def __init__(self, + name, + doc, + children, + requires=None, + properties=None, + callback=None, + callback_params=None): + + super(DynOptionDescription, self).__init__(name, + doc, + children, + requires, + properties) + # check children + set relation to this dynoptiondescription + for child in children: + if isinstance(child, OptionDescription): + if child.impl_get_group_type() != groups.master: + raise ConfigError(_('cannot set optiondescription in a ' + 'dynoptiondescription')) + for chld in child.impl_getchildren(setting_properties=undefined): + chld._impl_setsubdyn(self) + if child.impl_is_symlinkoption(): + raise ConfigError(_('cannot set symlinkoption in a ' + 'dynoptiondescription')) + child._impl_setsubdyn(self) + # add callback + self.impl_set_callback(callback, + callback_params) + + def _validate_callback(self, + callback, + callback_params): + if callback is None: + raise ConfigError(_('callback is mandatory for the dynoptiondescription "{}"' + '').format(self.impl_get_display_name())) + + def _impl_get_suffixes(self, + context, + setting_properties): + callback, callback_params = self.impl_get_callback() + values = carry_out_calculation(self, + context=context, + callback=callback, + callback_params=callback_params, + setting_properties=setting_properties) + if not isinstance(values, list): + raise ValueError(_('invalid suffix "{}" for option "{}", must be a list' + '').format(values, + self.impl_get_display_name())) + if len(values) > len(set(values)): + raise ValueError(_('DynOptionDescription callback return not unique value')) + for val in values: + if not isinstance(val, str) or re.match(NAME_REGEXP, val) is None: + raise ValueError(_('invalid suffix "{}" for option "{}"' + '').format(val, + self.impl_get_display_name())) + return values + diff --git a/tiramisu/option/masterslave.py b/tiramisu/option/masterslave.py index ecb7493..f06b3a1 100644 --- a/tiramisu/option/masterslave.py +++ b/tiramisu/option/masterslave.py @@ -19,6 +19,430 @@ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the whole pypy projet is under MIT licence # ____________________________________________________________ -from .optiondescription import MasterSlaves +import weakref +from ..i18n import _ +from ..setting import groups, undefined, log, debug +from .optiondescription import OptionDescription +from .option import Option +from ..error import SlaveError, PropertiesOptionError + + +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 = [] + master = children[0] + if not children: + raise ValueError(_('children is mandatory in masterslaves "{}"').format(name)) + for child in children[1:]: + if child.impl_getdefault() != []: + raise ValueError(_("not allowed default value for option {0} " + "in master/slave object {1}").format(child.impl_getname(), + name)) + slaves.append(child) + child._add_dependency(self) + for idx, child in enumerate(children): + if child.impl_is_symlinkoption(): # pragma: optional cover + raise ValueError(_("master group {0} shall not have " + "a symlinkoption").format(self.impl_getname())) + if not isinstance(child, Option): # pragma: optional cover + raise ValueError(_("master group {0} shall not have " + "a subgroup").format(self.impl_getname())) + 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(), self.impl_getname())) + # no empty property for save + if idx != 0: + properties = list(child._properties) + properties.remove('empty') + child._properties = tuple(properties) + callback, callback_params = master.impl_get_callback() + if callback is not None and callback_params != {}: + for callbacks in callback_params.values(): + 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 + for child in children: + child._master_slaves = weakref.ref(self) + master._add_dependency(self) + + 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, opt): + master = self._children[1][0] + if opt is not None and 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._children[1][1:]: + 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: + 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, + opt, + values, + setting_properties, + _commit=True, + force_permissive=False): + + for slave in self.getslaves(opt): + slave_path = slave.impl_getpath(values._getcontext()) + values.reset(slave, + slave_path, + setting_properties, + validate=False, + _commit=_commit, + force_permissive=force_permissive) + + def pop(self, + opt, + path, + values, + index, + setting_properties, + force_permissive=False): + + for slave in self.getslaves(opt): + slave_path = slave.impl_getpath(values._getcontext()) + slavelen = values._p_.get_max_length(slave_path) + if not values.is_default_owner(slave, + slave_path, + setting_properties, + validate_meta=False, + index=index, + force_permissive=force_permissive): + #FIXME # just for raise if needed + #multi = values.get_cached_value(slave, + # validate=False, + # validate_properties=False, + # ) + #if isinstance(multi, Exception): + # raise multi + if slavelen > index: + values._p_.resetvalue_index(slave_path, + index) + if slavelen > index + 1: + for idx in range(index + 1, slavelen): + values._p_.reduce_index(slave_path, + idx) + + + def getitem(self, + values, + opt, + path, + validate, + force_permissive, + trusted_cached_properties, + validate_properties, + setting_properties=undefined, + self_properties=undefined, + index=None, + check_frozen=False): + if self.is_master(opt): + return self._getmaster(values, + opt, + path, + validate, + force_permissive, + validate_properties, + self_properties, + index, + setting_properties, + check_frozen) + else: + return self._getslave(values, + opt, + path, + validate, + force_permissive, + trusted_cached_properties, + validate_properties, + setting_properties, + self_properties, + index, + check_frozen) + + def _getmaster(self, + values, + opt, + path, + validate, + force_permissive, + validate_properties, + self_properties, + index, + setting_properties, + check_frozen): + return values.get_cached_value(opt, + path=path, + validate=validate, + force_permissive=force_permissive, + self_properties=self_properties, + index=index, + setting_properties=setting_properties) + + def _getslave(self, values, opt, path, validate, force_permissive, + trusted_cached_properties, validate_properties, setting_properties, + self_properties, index, check_frozen): + """ + 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) + try: + mastervalue = values.get_cached_value(master, + path=masterp, + validate=validate, + force_permissive=force_permissive, + validate_properties=validate_properties, + self_properties=self_properties, + from_masterslave=True, + setting_properties=setting_properties, + check_frozen=check_frozen) + except PropertiesOptionError as mastervalue: + mastervalue.set_orig_opt(opt) + raise mastervalue + masterlen = len(mastervalue) + #self._master_is_meta = values._is_meta(master, masterp, force_permissive=force_permissive) + multi = list() # values._get_multi(opt, path) + if validate_properties: + props = context.cfgimpl_get_settings().validate_properties(opt, False, + check_frozen, + value=multi, + path=path, + force_permissive=force_permissive, + setting_properties=setting_properties) + if props: + return props + #FIXME shouldn't have index!!! + if index is None: + indexes = range(0, masterlen) + else: + indexes = [index] + for idx in indexes: + try: + value = values.get_cached_value(opt, + path, + validate, + force_permissive, + trusted_cached_properties, + validate_properties, + index=idx, + # not self_properties, + # depends to index + #self_properties=self_properties, + setting_properties=setting_properties, + from_masterslave=True, + check_frozen=check_frozen) + except PropertiesOptionError as perr: + err = perr + if index is None: + multi.append(err) + else: + multi = err + if index is None: + multi.append(value) + else: + multi = value + return multi + + def validate(self, + values, + opt, + index, + path, + setitem): + if self.is_master(opt): + #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(index, + slavelen, + slave.impl_getname(), + opt) + else: + val_len = self.get_length(values) + if isinstance(val_len, Exception): + return val_len + self.validate_slave_length(val_len, + index, + opt.impl_getname(), + opt, + setitem=setitem) + + def get_length(self, + values, + validate=True, + force_permissive=False, + master=None, + masterp=None, + setting_properties=undefined): + """get master len with slave option""" + if master is None: + master = self.getmaster(None) + if masterp is None: + masterp = master.impl_getpath(values._getcontext()) + value = self._getmaster(values, + master, + masterp, + validate, + force_permissive, + validate, + undefined, + None, + setting_properties, + False) + 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): + if debug: # pragma: no cover + log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, ' + 'setitem: {2}'.format(masterlen, valuelen, setitem)) + if not opt.impl_is_master_slaves('master'): + opt = self.getmaster(opt) + raise SlaveError(_("invalid len for the slave: {0}" + " which has {1} as master").format( + name, opt.impl_getname())) + + def reset_cache(self, + opt, + path, + obj, + type_, + resetted_opts): + context = obj._getcontext() + #FIXME pb avec dyn, devrait etre une option + mopt = self.getmaster(None) + mpath = mopt.impl_getpath(context) + mopt.reset_cache(mopt, + mpath, + obj, + type_, + resetted_opts) + for slave in self.getslaves(mopt): + spath = slave.impl_getpath(context) + slave.reset_cache(slave, + spath, + obj, + type_, + resetted_opts) + + def impl_getchild(self, + name, + setting_properties, + context=undefined, + dyn=True): + return super(MasterSlaves, self).impl_getchild(name, + setting_properties, + context, + dyn) + + def impl_validate(self, + context, + force_permissive, + setting_properties, + masterlen=None, + slavelen=None, + opt=None, + setitem=False): + values = context.cfgimpl_get_values() + if masterlen is None: + master = self.getmaster(opt) + masterp = master.impl_getpath(context) + + mastervalue = values.get_cached_value(master, path=masterp, + force_permissive=force_permissive, + setting_properties=setting_properties) + if isinstance(mastervalue, Exception): + return mastervalue + masterlen = len(mastervalue) + else: + master = opt + if slavelen is not None: + self.validate_slave_length(masterlen, slavelen, opt.impl_getname(), master, setitem=setitem) + else: + for slave in self.getslaves(master): + slave_path = slave.impl_getpath(context) + slavelen = values._p_.get_max_length(slave_path) + self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), master) + + def impl_validate_value(self, + option, + value, + context): + if option.impl_is_master_slaves('master') and isinstance(value, list): + if len(value) < context._impl_length: + return ValueError(_('cannot reduce length of master "{}"' + '').format(option.impl_get_display_name())) diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index 6530d6e..5b107ea 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -19,25 +19,14 @@ # the whole pypy projet is under MIT licence # ____________________________________________________________ from copy import copy -import re -import weakref from ..i18n import _ -from ..setting import groups, undefined, owners, log, debug +from ..setting import groups, undefined, owners from .baseoption import BaseOption -from .option import Option, ALLOWED_CONST_LIST -#from . import MasterSlaves -from ..error import ConfigError, ConflictError, SlaveError, PropertiesOptionError -from ..autolib import carry_out_calculation - - -NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$') - -import sys -if sys.version_info[0] >= 3: # pragma: no cover - xrange = range -del(sys) +from .option import ALLOWED_CONST_LIST +from .syndynoptiondescription import SynDynOptionDescription +from ..error import ConfigError, ConflictError class CacheOptionDescription(BaseOption): @@ -138,7 +127,7 @@ class CacheOptionDescription(BaseOption): require_opt.impl_getname(), option.impl_getname())) if init: if len(cache_option) != len(set(cache_option)): - for idx in xrange(1, len(cache_option) + 1): + for idx in range(1, len(cache_option) + 1): opt = cache_option.pop(0) if opt in cache_option: raise ConflictError(_('duplicate option: {0}').format(opt)) @@ -234,7 +223,7 @@ class OptionDescriptionWalk(CacheOptionDescription): dynopt): found = False spath = path.split('.') - for length in xrange(1, len(spath)): + for length in range(1, len(spath)): subpath = '.'.join(spath[0:length]) subopt = self.impl_get_opt_by_path(subpath) if dynopt == subopt: @@ -243,7 +232,7 @@ class OptionDescriptionWalk(CacheOptionDescription): if not found: # pragma: no cover raise ConfigError(_('cannot find dynpath')) subpath = subpath + suffix - for slength in xrange(length, len(spath)): + for slength in range(length, len(spath)): subpath = subpath + '.' + spath[slength] + suffix return subpath @@ -445,7 +434,7 @@ class OptionDescription(OptionDescriptionWalk): for child in children: name = child.impl_getname() child_names.append(name) - if isinstance(child, DynOptionDescription): + if child.impl_is_dynoptiondescription(): dynopt_names.append(name) #better performance like this @@ -503,536 +492,3 @@ class OptionDescription(OptionDescriptionWalk): value, context): pass - - -class DynOptionDescription(OptionDescription): - - def __init__(self, - name, - doc, - children, - requires=None, - properties=None, - callback=None, - callback_params=None): - - super(DynOptionDescription, self).__init__(name, - doc, - children, - requires, - properties) - # check children + set relation to this dynoptiondescription - for child in children: - if isinstance(child, OptionDescription): - if child.impl_get_group_type() != groups.master: - raise ConfigError(_('cannot set optiondescription in a ' - 'dynoptiondescription')) - for chld in child.impl_getchildren(setting_properties=undefined): - chld._impl_setsubdyn(self) - if child.impl_is_symlinkoption(): - raise ConfigError(_('cannot set symlinkoption in a ' - 'dynoptiondescription')) - child._impl_setsubdyn(self) - # add callback - self.impl_set_callback(callback, - callback_params) - - def _validate_callback(self, - callback, - callback_params): - if callback is None: - raise ConfigError(_('callback is mandatory for the dynoptiondescription "{}"' - '').format(self.impl_get_display_name())) - - def _impl_get_suffixes(self, - context, - setting_properties): - callback, callback_params = self.impl_get_callback() - values = carry_out_calculation(self, - context=context, - callback=callback, - callback_params=callback_params, - setting_properties=setting_properties) - if not isinstance(values, list): - raise ValueError(_('invalid suffix "{}" for option "{}", must be a list' - '').format(values, - self.impl_get_display_name())) - if len(values) > len(set(values)): - raise ValueError(_('DynOptionDescription callback return not unique value')) - for val in values: - if not isinstance(val, str) or re.match(NAME_REGEXP, val) is None: - raise ValueError(_('invalid suffix "{}" for option "{}"' - '').format(val, - self.impl_get_display_name())) - return values - - -class SynDynOptionDescription(object): - __slots__ = ('_opt', - '_name', - '_suffix') - - def __init__(self, - opt, - name, - suffix): - - self._opt = opt - self._name = name - self._suffix = suffix - - def __getattr__(self, name): - return getattr(self._opt, name) - - def impl_getchild(self, - name, - setting_properties, - context): - if name.endswith(self._suffix): - oname = name[:-len(self._suffix)] - child = self._children[1][self._children[0].index(oname)] - return self._impl_get_dynchild(child, - self._suffix) - raise AttributeError(_('unknown Option {0} ' - 'in SynDynOptionDescription {1}' - '').format(name, self.impl_getname())) - - def impl_getname(self): - return self._name - - def impl_getchildren(self, - setting_properties, - dyn=True, - context=undefined): - children = [] - for child in self._opt.impl_getchildren(setting_properties): - yield(self._opt._impl_get_dynchild(child, - self._suffix)) - - def impl_getpath(self, context): - path = self.impl_getopt().impl_getpath(context).split('.') - path[-1] += self._suffix - path.append(self._name) - return '.'.join(path) - - def impl_getopt(self): - return self._opt - - -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 = [] - master = children[0] - if not children: - raise ValueError(_('children is mandatory in masterslaves "{}"').format(name)) - for child in children[1:]: - if child.impl_getdefault() != []: - raise ValueError(_("not allowed default value for option {0} " - "in master/slave object {1}").format(child.impl_getname(), - name)) - slaves.append(child) - child._add_dependency(self) - for idx, child in enumerate(children): - if child.impl_is_symlinkoption(): # pragma: optional cover - raise ValueError(_("master group {0} shall not have " - "a symlinkoption").format(self.impl_getname())) - if not isinstance(child, Option): # pragma: optional cover - raise ValueError(_("master group {0} shall not have " - "a subgroup").format(self.impl_getname())) - 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(), self.impl_getname())) - # no empty property for save - if idx != 0: - properties = list(child._properties) - properties.remove('empty') - child._properties = tuple(properties) - callback, callback_params = master.impl_get_callback() - if callback is not None and callback_params != {}: - for callbacks in callback_params.values(): - 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 - for child in children: - child._master_slaves = weakref.ref(self) - master._add_dependency(self) - - 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, opt): - master = self._children[1][0] - if opt is not None and 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._children[1][1:]: - 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: - 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, - opt, - values, - setting_properties, - _commit=True, - force_permissive=False): - - for slave in self.getslaves(opt): - slave_path = slave.impl_getpath(values._getcontext()) - values.reset(slave, - slave_path, - setting_properties, - validate=False, - _commit=_commit, - force_permissive=force_permissive) - - def pop(self, - opt, - path, - values, - index, - setting_properties, - force_permissive=False): - - for slave in self.getslaves(opt): - slave_path = slave.impl_getpath(values._getcontext()) - slavelen = values._p_.get_max_length(slave_path) - if not values.is_default_owner(slave, - slave_path, - setting_properties, - validate_meta=False, - index=index, - force_permissive=force_permissive): - #FIXME # just for raise if needed - #multi = values.get_cached_value(slave, - # validate=False, - # validate_properties=False, - # ) - #if isinstance(multi, Exception): - # raise multi - if slavelen > index: - values._p_.resetvalue_index(slave_path, - index) - if slavelen > index + 1: - for idx in xrange(index + 1, slavelen): - values._p_.reduce_index(slave_path, - idx) - - - def getitem(self, - values, - opt, - path, - validate, - force_permissive, - trusted_cached_properties, - validate_properties, - setting_properties=undefined, - self_properties=undefined, - index=None, - check_frozen=False): - if self.is_master(opt): - return self._getmaster(values, - opt, - path, - validate, - force_permissive, - validate_properties, - self_properties, - index, - setting_properties, - check_frozen) - else: - return self._getslave(values, - opt, - path, - validate, - force_permissive, - trusted_cached_properties, - validate_properties, - setting_properties, - self_properties, - index, - check_frozen) - - def _getmaster(self, - values, - opt, - path, - validate, - force_permissive, - validate_properties, - self_properties, - index, - setting_properties, - check_frozen): - return values.get_cached_value(opt, - path=path, - validate=validate, - force_permissive=force_permissive, - self_properties=self_properties, - index=index, - setting_properties=setting_properties) - - def _getslave(self, values, opt, path, validate, force_permissive, - trusted_cached_properties, validate_properties, setting_properties, - self_properties, index, check_frozen): - """ - 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) - try: - mastervalue = values.get_cached_value(master, - path=masterp, - validate=validate, - force_permissive=force_permissive, - validate_properties=validate_properties, - self_properties=self_properties, - from_masterslave=True, - setting_properties=setting_properties, - check_frozen=check_frozen) - except PropertiesOptionError as mastervalue: - mastervalue.set_orig_opt(opt) - raise mastervalue - masterlen = len(mastervalue) - #self._master_is_meta = values._is_meta(master, masterp, force_permissive=force_permissive) - multi = list() # values._get_multi(opt, path) - if validate_properties: - props = context.cfgimpl_get_settings().validate_properties(opt, False, - check_frozen, - value=multi, - path=path, - force_permissive=force_permissive, - setting_properties=setting_properties) - if props: - return props - #FIXME shouldn't have index!!! - if index is None: - indexes = xrange(0, masterlen) - else: - indexes = [index] - for idx in indexes: - try: - value = values.get_cached_value(opt, - path, - validate, - force_permissive, - trusted_cached_properties, - validate_properties, - index=idx, - # not self_properties, - # depends to index - #self_properties=self_properties, - setting_properties=setting_properties, - from_masterslave=True, - check_frozen=check_frozen) - except PropertiesOptionError as perr: - err = perr - if index is None: - multi.append(err) - else: - multi = err - if index is None: - multi.append(value) - else: - multi = value - return multi - - def validate(self, - values, - opt, - index, - path, - setitem): - if self.is_master(opt): - #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(index, - slavelen, - slave.impl_getname(), - opt) - else: - val_len = self.get_length(values) - if isinstance(val_len, Exception): - return val_len - self.validate_slave_length(val_len, - index, - opt.impl_getname(), - opt, - setitem=setitem) - - def get_length(self, - values, - validate=True, - force_permissive=False, - master=None, - masterp=None, - setting_properties=undefined): - """get master len with slave option""" - if master is None: - master = self.getmaster(None) - if masterp is None: - masterp = master.impl_getpath(values._getcontext()) - value = self._getmaster(values, - master, - masterp, - validate, - force_permissive, - validate, - undefined, - None, - setting_properties, - False) - 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): - if debug: # pragma: no cover - log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, ' - 'setitem: {2}'.format(masterlen, valuelen, setitem)) - if not opt.impl_is_master_slaves('master'): - opt = self.getmaster(opt) - raise SlaveError(_("invalid len for the slave: {0}" - " which has {1} as master").format( - name, opt.impl_getname())) - - def reset_cache(self, - opt, - path, - obj, - type_, - resetted_opts): - context = obj._getcontext() - #FIXME pb avec dyn, devrait etre une option - mopt = self.getmaster(None) - mpath = mopt.impl_getpath(context) - mopt.reset_cache(mopt, - mpath, - obj, - type_, - resetted_opts) - for slave in self.getslaves(mopt): - spath = slave.impl_getpath(context) - slave.reset_cache(slave, - spath, - obj, - type_, - resetted_opts) - - def impl_getchild(self, - name, - setting_properties, - context=undefined, - dyn=True): - return super(MasterSlaves, self).impl_getchild(name, - setting_properties, - context, - dyn) - - def impl_validate(self, - context, - force_permissive, - setting_properties, - masterlen=None, - slavelen=None, - opt=None, - setitem=False): - values = context.cfgimpl_get_values() - if masterlen is None: - master = self.getmaster(opt) - masterp = master.impl_getpath(context) - - mastervalue = values.get_cached_value(master, path=masterp, - force_permissive=force_permissive, - setting_properties=setting_properties) - if isinstance(mastervalue, Exception): - return mastervalue - masterlen = len(mastervalue) - else: - master = opt - if slavelen is not None: - self.validate_slave_length(masterlen, slavelen, opt.impl_getname(), master, setitem=setitem) - else: - for slave in self.getslaves(master): - slave_path = slave.impl_getpath(context) - slavelen = values._p_.get_max_length(slave_path) - self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), master) - - def impl_validate_value(self, - option, - value, - context): - if option.impl_is_master_slaves('master') and isinstance(value, list): - if len(value) < context._impl_length: - return ValueError(_('cannot reduce length of master "{}"' - '').format(option.impl_get_display_name())) diff --git a/tiramisu/option/syndynoptiondescription.py b/tiramisu/option/syndynoptiondescription.py new file mode 100644 index 0000000..a9facfb --- /dev/null +++ b/tiramisu/option/syndynoptiondescription.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 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 undefined + + +class SynDynOptionDescription(object): + __slots__ = ('_opt', + '_name', + '_suffix') + + def __init__(self, + opt, + name, + suffix): + + self._opt = opt + self._name = name + self._suffix = suffix + + def __getattr__(self, name): + return getattr(self._opt, name) + + def impl_getchild(self, + name, + setting_properties, + context): + if name.endswith(self._suffix): + oname = name[:-len(self._suffix)] + child = self._children[1][self._children[0].index(oname)] + return self._impl_get_dynchild(child, + self._suffix) + raise AttributeError(_('unknown Option {0} ' + 'in SynDynOptionDescription {1}' + '').format(name, self.impl_getname())) + + def impl_getname(self): + return self._name + + def impl_getchildren(self, + setting_properties, + dyn=True, + context=undefined): + children = [] + for child in self._opt.impl_getchildren(setting_properties): + yield(self._opt._impl_get_dynchild(child, + self._suffix)) + + def impl_getpath(self, context): + path = self.impl_getopt().impl_getpath(context).split('.') + path[-1] += self._suffix + path.append(self._name) + return '.'.join(path) + + def impl_getopt(self): + return self._opt