not Params object for callback, validator and calculated values for choiceoption

This commit is contained in:
2018-04-15 21:13:16 +02:00
parent 2b08ab35d6
commit 54b57968bc
20 changed files with 390 additions and 327 deletions

View File

@ -13,6 +13,7 @@
# 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/>.
from .config import Config, MetaConfig, GroupConfig
from .function import Params, ParamOption, ParamValue, ParamContext
from .option import *
from .error import APIError
from .api import getapi
@ -20,7 +21,11 @@ from .option import __all__ as all_options
from .setting import owners, undefined
allfuncs = ['MetaConfig',
allfuncs = ['Params',
'ParamOption',
'ParamValue',
'ParamContext',
'MetaConfig',
'GroupConfig',
'Config',
'getapi',

View File

@ -23,9 +23,73 @@ from .i18n import _
from .setting import undefined
from .option.symlinkoption import DynSymLinkOption
from .storage import get_default_values_storages, get_default_settings_storages
from .function import ParamValue, ParamContext, ParamIndex
# ____________________________________________________________
def manager_callback(callbk,
option,
index,
orig_value,
config_bag,
context):
"""replace Param by true value"""
if isinstance(callbk, ParamValue):
return callbk.value
if isinstance(callbk, ParamIndex):
return index
if context is undefined:
return undefined
if isinstance(callbk, ParamContext):
#Not an option, set full context
return context.duplicate(force_values=get_default_values_storages(),
force_settings=get_default_settings_storages())
opt = callbk.option
if opt.issubdyn():
opt = DynSymLinkOption(opt,
option._rootpath,
option.impl_getsuffix())
path = opt.impl_getpath(context)
else:
path = context.cfgimpl_get_description().impl_get_path_by_opt(opt)
# don't validate if option is option that we tried to validate
sconfig_bag = config_bag.copy('nooption')
sconfig_bag.option = opt
sconfig_bag.force_permissive = True
if index is not None and opt.impl_is_master_slaves() and \
opt.impl_get_master_slaves().in_same_group(option):
if opt == option:
index_ = None
with_index = False
elif opt.impl_is_master_slaves('slave'):
index_ = index
with_index = False
else:
index_ = None
with_index = True
else:
index_ = None
with_index = False
if opt == option and orig_value is not undefined and \
(not opt.impl_is_master_slaves('slave') or index is None):
return orig_value
if opt == option:
sconfig_bag.validate = False
try:
# get value
value = context.getattr(path,
index_,
sconfig_bag)
if with_index:
return value[index]
return value
except PropertiesOptionError as err:
if callbk.notraiseproperty:
raise err
raise ConfigError(_('unable to carry out a calculation for "{}"'
', {}').format(option.impl_get_display_name(), err))
def carry_out_calculation(option,
context,
callback,
@ -147,75 +211,36 @@ def carry_out_calculation(option,
args = []
kwargs = {}
# if callback_params has a callback, launch several time calculate()
master_slave = False
# multi's option should have same value for all option
if option.issubdyn():
#FIXME why here? should be ParamSuffix !
kwargs['suffix'] = option.impl_getsuffix()
for key, callbacks in callback_params.items():
for callbk in callbacks:
if not isinstance(callbk, tuple):
# callbk is a value and not a multi
value = callbk
elif context is undefined:
return undefined
elif callbk[0] is None: # pragma: optional cover
#Not an option, set full context
value = context.duplicate(force_values=get_default_values_storages(),
force_settings=get_default_settings_storages())
elif callbk[0] == 'index':
value = index
else:
# callbk is something link (opt, True|False)
opt, force_permissive = callbk
if opt.issubdyn():
opt = DynSymLinkOption(opt,
option._rootpath,
option.impl_getsuffix())
path = opt.impl_getpath(context)
else:
path = context.cfgimpl_get_description().impl_get_path_by_opt(opt)
# don't validate if option is option that we tried to validate
sconfig_bag = config_bag.copy('nooption')
sconfig_bag.option = opt
sconfig_bag.force_permissive = True
if index is not None and opt.impl_is_master_slaves() and \
opt.impl_get_master_slaves().in_same_group(option):
if opt == option:
index_ = None
with_index = False
elif opt.impl_is_master_slaves('slave'):
index_ = index
with_index = False
else:
index_ = None
with_index = True
else:
index_ = None
with_index = False
if opt == option and orig_value is not undefined and \
(not opt.impl_is_master_slaves('slave') or index is None):
value = orig_value
else:
if opt == option:
sconfig_bag.validate = False
try:
# get value
value = context.getattr(path,
index_,
sconfig_bag)
if with_index:
value = value[index]
except PropertiesOptionError as err:
if force_permissive:
continue
raise ConfigError(_('unable to carry out a calculation for "{}"'
', {}').format(option.impl_get_display_name(), err))
if key == '':
if callback_params:
for callbk in callback_params.args:
try:
value = manager_callback(callbk,
option,
index,
orig_value,
config_bag,
context)
if value is undefined:
return undefined
args.append(value)
else:
except PropertiesOptionError:
pass
for key, callbk in callback_params.kwargs.items():
try:
value = manager_callback(callbk,
option,
index,
orig_value,
config_bag,
context)
if value is undefined:
return undefined
kwargs[key] = value
except PropertiesOptionError:
pass
ret = calculate(option,
callback,
is_validator,

73
tiramisu/function.py Normal file
View File

@ -0,0 +1,73 @@
# Copyright (C) 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/>.
from .i18n import _
class Params:
__slots__ = ('args', 'kwargs')
def __init__(self, args=None, kwargs=None):
if args is None:
args = tuple()
if kwargs is None:
kwargs = {}
if isinstance(args, Param):
args = (args,)
else:
if not isinstance(args, tuple):
raise ValueError(_('args in params must be a tuple'))
for arg in args:
if not isinstance(arg, Param):
raise ValueError(_('arg in params must be a Param'))
if not isinstance(kwargs, dict):
raise ValueError(_('kwargs in params must be a dict'))
for arg in kwargs.values():
if not isinstance(arg, Param):
raise ValueError(_('arg in params must be a Param'))
self.args = args
self.kwargs = kwargs
class Param:
pass
class ParamOption(Param):
__slots__ = ('option', 'notraisepropertyerror')
def __init__(self, option, notraisepropertyerror=False):
if option.impl_is_symlinkoption():
cur_opt = option.impl_getopt()
else:
cur_opt = option
if not isinstance(notraisepropertyerror, bool):
raise ValueError(_('param must have a boolean'
' not a {} for notraisepropertyerror'
).format(type(notraisepropertyerror)))
self.option = cur_opt
self.notraiseproperty = notraisepropertyerror
class ParamValue(Param):
__slots__ = ('value',)
def __init__(self, value):
self.value = value
class ParamContext(Param):
__slots__ = tuple()
class ParamIndex(Param):
__slots__ = tuple()

View File

@ -22,10 +22,12 @@ import re
from types import FunctionType
import weakref
from inspect import signature
from itertools import chain
from ..i18n import _
from ..setting import undefined
from ..error import ConfigError, display_list
from ..function import Params, ParamContext, ParamOption, ParamIndex
STATIC_TUPLE = frozenset()
@ -54,61 +56,17 @@ def validate_calculator(callback,
callbackoption):
"""validate function and parameter set for callback, validation, ...
"""
def _validate_option(option):
#validate option
if not isinstance(option, OnlyOption):
raise ValueError(_('{}_params must have an option'
' not a {} for first argument'
).format(type_, type(option)))
if option.impl_is_symlinkoption():
cur_opt = option.impl_getopt()
else:
cur_opt = option
if cur_opt != callbackoption:
cur_opt._add_dependency(callbackoption)
callbackoption._has_dependency = True
def _validate_force_permissive(force_permissive):
#validate force_permissive
if not isinstance(force_permissive, bool):
raise ValueError(_('{}_params must have a boolean'
' not a {} for second argument'
).format(type_,
type(force_permissive)))
def _validate_calculator(callbk):
if isinstance(callbk, tuple):
if len(callbk) == 1:
if callbk not in ((None,), ('index',)):
raise ValueError(_('{0}_params with length of '
'tuple as 1 must only have '
'None as first value').format(type_))
if callbk == ((None,)):
callbackoption._has_calc_context = True
return
elif len(callbk) != 2:
raise ValueError(_('{0}_params must only have 1 or 2 '
'as length').format(type_))
option, force_permissive = callbk
_validate_option(option)
_validate_force_permissive(force_permissive)
if not isinstance(callback, FunctionType):
raise ValueError(_('{0} must be a function').format(type_))
if callback_params is not None:
if not isinstance(callback_params, dict):
raise ValueError(_('{0}_params must be a dict').format(type_))
for key, callbacks in callback_params.items():
if key != '' and len(callbacks) != 1:
raise ValueError(_("{0}_params with key {1} mustn't have "
"length different to 1").format(type_,
key))
if not isinstance(callbacks, tuple):
raise ValueError(_('{0}_params must be tuple for key "{1}"'
).format(type_, key))
for callbk in callbacks:
_validate_calculator(callbk)
if not isinstance(callback_params, Params):
raise ValueError(_('{0}_params must be a params').format(type_))
for param in chain(callback_params.args, callback_params.kwargs.values()):
if isinstance(param, ParamContext):
callbackoption._has_calc_context = True
elif isinstance(param, ParamOption):
param.option._add_dependency(callbackoption)
callbackoption._has_dependency = True
#____________________________________________________________
@ -193,31 +151,30 @@ class Base(object):
def _get_parameters_args(self,
calculator_params,
add_value):
args = set()
kwargs = set()
# add value as first argument
if add_value:
args.add('value')
if self.impl_is_dynoptiondescription():
kwargs.add('suffix')
if calculator_params is not None:
for param in calculator_params.keys():
if param == '':
for idx, _ in enumerate(calculator_params['']):
# construct an appropriate name
args.add('param{}'.format(idx))
else:
kwargs.add(param)
if calculator_params:
for idx in range(len(calculator_params.args)):
# construct an appropriate name
args.add('param{}'.format(idx))
for param in calculator_params.kwargs:
kwargs.add(param)
return args, kwargs
def _build_calculator_params(self,
calculator,
calculator_params,
add_value=False):
"""
:add_value: add value as first argument for validator
"""
is_multi = self.impl_is_optiondescription() or self.impl_is_multi()
if calculator_params is None:
calculator_params = {}
is_multi = self.impl_is_dynoptiondescription() or self.impl_is_multi()
func_args, func_kwargs, func_positional, func_keyword = self._get_function_args(calculator)
calculator_args, calculator_kwargs = self._get_parameters_args(calculator_params, add_value)
# remove knowned kwargs
@ -242,6 +199,7 @@ class Base(object):
func_kwargs_pop.add(func_kwargs_left.pop())
calculator_args.pop()
func_kwargs -= func_kwargs_pop
# func_positional or keyword is True, so assume all args or kwargs are satisfy
if func_positional:
calculator_args = set()
if func_keyword:
@ -255,26 +213,27 @@ class Base(object):
has_index = False
if is_multi and func_args:
# there is extra args/kwargs
if not self.impl_is_optiondescription() and is_multi:
params = list(calculator_params.get('', tuple()))
if not self.impl_is_dynoptiondescription():
if calculator_params is None:
calculator_params = Params()
params = list(calculator_params.args)
if add_value:
# only for validator
has_self = True
params.append((self, False))
params.append(ParamOption(self))
func_args.pop()
if func_args:
has_index = True
params.append(('index',))
params.append(ParamIndex())
func_args.pop()
calculator_params[''] = tuple(params)
calculator_params.args = tuple(params)
if func_args:
raise ConfigError(_('missing those arguments "{}" in function "{}" for "{}"'
'').format(display_list(list(func_args)),
calculator.__name__,
self.impl_get_display_name()))
if not self.impl_is_optiondescription() and self.impl_is_multi():
if not has_index and 'index' in func_kwargs:
calculator_params['index'] = (('index',),)
if not self.impl_is_dynoptiondescription() and is_multi and \
not has_index and 'index' in func_kwargs:
calculator_params.kwargs['index'] = ParamIndex()
return calculator_params
def impl_has_dependency(self,
@ -302,19 +261,15 @@ class Base(object):
options.add(weakref.ref(option))
self._dependencies = tuple(options)
def impl_set_callback(self,
callback,
callback_params=None,
_init=False):
def _impl_set_callback(self,
callback,
callback_params=None):
if callback is None and callback_params is not None:
raise ValueError(_("params defined for a callback function but "
"no callback defined"
' yet for option "{0}"').format(
self.impl_getname()))
if not _init and self.impl_get_callback()[0] is not None:
raise ConfigError(_("a callback is already set for {0}, "
"cannot set another one's").format(self.impl_getname()))
self._validate_calculator(callback,
callback_params)
if callback is not None:

View File

@ -61,8 +61,8 @@ class DynOptionDescription(OptionDescription):
'dynoptiondescription'))
child._setsubdyn(self)
# add callback
self.impl_set_callback(callback,
callback_params)
self._impl_set_callback(callback,
callback_params)
def _validate_calculator(self,
callback,

View File

@ -20,6 +20,7 @@
# the whole pypy projet is under MIT licence
# ____________________________________________________________
import weakref
from itertools import chain
from ..i18n import _
@ -27,6 +28,7 @@ from ..setting import groups, undefined
from .optiondescription import OptionDescription
from .option import Option
from ..error import SlaveError, PropertiesOptionError
from ..function import ParamOption
class MasterSlaves(OptionDescription):
@ -74,13 +76,12 @@ class MasterSlaves(OptionDescription):
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 != {}:
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"))
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"))
def is_master(self, opt):
master = self._children[0][0]

View File

@ -29,7 +29,7 @@ from ..setting import log, undefined, debug
from ..autolib import carry_out_calculation
from ..error import (ConfigError, ValueWarning, PropertiesOptionError,
display_list)
from itertools import combinations
from ..function import Params, ParamValue
ALLOWED_CONST_LIST = ['_cons_not_equal']
@ -157,9 +157,8 @@ class Option(OnlyOption):
default = tuple(default)
_setattr(self, '_default', default)
self.impl_set_callback(callback,
callback_params,
_init=True)
self._impl_set_callback(callback,
callback_params)
def impl_is_multi(self):
return getattr(self, '_multi', 1) != 1
@ -221,19 +220,15 @@ class Option(OnlyOption):
_index):
validator, validator_params = self.impl_get_validator()
if validator is not None:
if validator_params != {}:
validator_params_ = {}
for val_param, values in validator_params.items():
validator_params_[val_param] = values
#inject value in calculation
if '' in validator_params_:
lst = list(validator_params_[''])
lst.insert(0, val)
validator_params_[''] = tuple(lst)
else:
validator_params_[''] = (val,)
#inject value in calculation
if validator_params is None:
args = []
kwargs = None
else:
validator_params_ = {'': (val,)}
args = list(validator_params.args)
kwargs = validator_params.kwargs
args.insert(0, ParamValue(val))
validator_params_ = Params(tuple(args), kwargs)
# Raise ValueError if not valid
carry_out_calculation(current_opt,
context=context,