not Params object for callback, validator and calculated values for choiceoption
This commit is contained in:
@ -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',
|
||||
|
@ -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
73
tiramisu/function.py
Normal 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()
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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]
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user