add Calculation to values

This commit is contained in:
2019-09-28 16:32:48 +02:00
parent bb2ecc94d9
commit fd50913466
17 changed files with 1274 additions and 259 deletions

View File

@ -111,7 +111,7 @@ class Calculation:
'has_index')
def __init__(self,
function: Callable,
params: Optional[Params]=None,
params: Params=Params(),
help_function: Optional[Callable]=None):
assert isinstance(function, Callable), _('first argument ({0}) must be a function').format(function)
if help_function:
@ -120,14 +120,11 @@ class Calculation:
else:
self.help_function = None
self.function = function
if params:
self.params = params
for arg in chain(self.params.args, self.params.kwargs.values()):
if isinstance(arg, ParamIndex):
self.has_index = True
break
else:
self.has_index = False
self.params = params
for arg in chain(self.params.args, self.params.kwargs.values()):
if isinstance(arg, ParamIndex):
self.has_index = True
break
else:
self.has_index = False

View File

@ -32,7 +32,7 @@ from .syndynoptiondescription import SynDynLeadership
from .baseoption import BaseOption
from .option import Option
from ..error import RequirementError
from ..autolib import ParamOption
from ..autolib import Calculation, ParamOption
class Leadership(OptionDescription):
@ -69,11 +69,13 @@ class Leadership(OptionDescription):
'"{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 follower option "{0}" '
'in leadership "{1}"'
'').format(child.impl_get_display_name(),
self.impl_get_display_name()))
if idx != 0:
default = child.impl_getdefault()
if default != [] and not isinstance(default, Calculation):
raise ValueError(_('not allowed default value for follower option "{0}" '
'in leadership "{1}"'
'').format(child.impl_get_display_name(),
self.impl_get_display_name()))
if idx != 0:
# remove empty property for follower
child_properties = list(child._properties)
@ -82,12 +84,15 @@ class Leadership(OptionDescription):
followers.append(child)
child._add_dependency(self)
child._leadership = weakref.ref(self)
callback, callback_params = leader.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 followers:
raise ValueError(_("callback of leader's option shall "
"not refered to a follower's ones"))
if __debug__:
callback, callback_params = leader.impl_get_callback()
options = []
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 followers:
raise ValueError(_("callback of leader's option shall "
"not refered to a follower's ones"))
# leader should not have requires, only Leadership should have
# so move requires to Leadership
# if Leadership has requires too, cannot manage this move so raises

View File

@ -22,11 +22,12 @@
import warnings
import weakref
from typing import Any, List, Callable, Optional, Dict, Union, Tuple
from itertools import chain
from .baseoption import BaseOption, submulti, STATIC_TUPLE
from ..i18n import _
from ..setting import undefined, OptionBag, Undefined
from ..autolib import carry_out_calculation, Params, ParamValue
from ..autolib import Calculation, carry_out_calculation, Params, ParamValue, ParamContext, ParamOption
from ..error import (ConfigError, ValueWarning, ValueErrorWarning, PropertiesOptionError,
ValueOptionError, display_list)
from .syndynoption import SynDynOption
@ -123,22 +124,31 @@ class Option(BaseOption):
_setattr(self, '_allow_empty_list', allow_empty_list)
if is_multi and default_multi is not None:
def test_multi_value(value):
if isinstance(value, Calculation):
return
try:
self._validate(value,
undefined)
except ValueError as err:
raise ValueError(_("invalid default_multi value {0} "
"for option {1}: {2}").format(str(value),
self.impl_getname(),
str(err)))
str_err = str(err)
if not str_err:
raise ValueError(_('invalid default_multi value "{0}" '
'for option "{1}"').format(str(value),
self.impl_get_display_name()))
else:
raise ValueError(_('invalid default_multi value "{0}" '
'for option "{1}", {2}').format(str(value),
self.impl_get_display_name(),
str_err))
if _multi is submulti:
if not isinstance(default_multi, list):
raise ValueError(_('invalid default_multi value "{0}" '
'for option "{1}", must be a list for a submulti'
'').format(str(default_multi),
self.impl_get_display_name()))
for value in default_multi:
test_multi_value(value)
if not isinstance(default_multi, Calculation):
if not isinstance(default_multi, list):
raise ValueError(_('invalid default_multi value "{0}" '
'for option "{1}", must be a list for a submulti'
'').format(str(default_multi),
self.impl_get_display_name()))
for value in default_multi:
test_multi_value(value)
else:
test_multi_value(default_multi)
_setattr(self, '_default_multi', default_multi)
@ -151,15 +161,35 @@ class Option(BaseOption):
undefined)
self.impl_validate(default,
option_bag)
self.value_dependencies(default)
if (is_multi and default != []) or \
(not is_multi and default is not None):
if is_multi:
if is_multi and isinstance(default, list):
default = tuple(default)
_setattr(self, '_default', default)
self._impl_set_callback(callback,
callback_params)
def value_dependencies(self,
value: Any) -> Any:
if isinstance(value, list):
for val in value:
if isinstance(value, list):
self.value_dependencies(val)
elif isinstance(value, Calculation):
self.value_dependency(val)
elif isinstance(value, Calculation):
self.value_dependency(value)
def value_dependency(self,
value: Any) -> Any:
for param in chain(value.params.args, value.params.kwargs.values()):
if isinstance(param, ParamContext):
self._has_calc_context = True
elif isinstance(param, ParamOption):
param.option._add_dependency(self)
#__________________________________________________________________________
# option's information
@ -195,7 +225,7 @@ class Option(BaseOption):
else:
default = None
else:
if is_multi:
if is_multi and isinstance(default, list):
default = list(default)
return default
@ -287,6 +317,8 @@ class Option(BaseOption):
if isinstance(_value, list):
raise ValueError(_('which must not be a list').format(_value,
self.impl_get_display_name()))
if isinstance(_value, Calculation) and config_bag is undefined:
return
if _value is not None:
if check_error:
# option validation
@ -327,11 +359,15 @@ class Option(BaseOption):
else:
do_validation(val,
force_index)
elif isinstance(value, Calculation) and config_bag is undefined:
pass
elif not isinstance(value, list):
raise ValueError(_('which must be a list'))
elif self.impl_is_submulti():
for err_index, lval in enumerate(value):
_is_not_unique(lval)
if isinstance(lval, Calculation):
continue
if not isinstance(lval, list):
raise ValueError(_('which "{}" must be a list of list'
'').format(lval))
@ -344,7 +380,7 @@ class Option(BaseOption):
do_validation(val,
err_index)
if not is_warnings_only or not check_error:
if (not is_warnings_only or not check_error) and not isinstance(value, Calculation):
self.valid_consistency(option_bag,
value,
check_error,
@ -448,9 +484,12 @@ class Option(BaseOption):
undefined,
None,
undefined)
self.impl_validate(self.impl_getdefault(),
default = self.impl_getdefault()
if isinstance(default, tuple):
default = list(default)
self.impl_validate(default,
option_bag)
self.impl_validate(self.impl_getdefault(),
self.impl_validate(default,
option_bag,
check_error=False)
if func != '_cons_not_equal':

View File

@ -19,7 +19,7 @@ import weakref
from typing import Optional, Any, Callable
from .error import ConfigError, PropertiesOptionError, RequirementError
from .setting import owners, undefined, forbidden_owners, OptionBag, ConfigBag
from .autolib import carry_out_calculation, Params
from .autolib import Calculation, carry_out_calculation, Params
from .i18n import _
@ -109,6 +109,17 @@ class Values(object):
return True
return False
def _do_value_list(self,
value: Any,
option_bag: OptionBag):
for val in value:
if isinstance(val, (list, tuple)):
yield list(self._do_value_list(val, option_bag))
elif isinstance(val, Calculation):
yield val.execute(option_bag)
else:
yield val
def getvalue(self,
option_bag):
"""actually retrieves the value
@ -118,6 +129,7 @@ class Values(object):
:returns: value
"""
# get owner and value from store
# index allowed only for follower
index = option_bag.index
@ -130,11 +142,21 @@ class Values(object):
owners.default,
index=_index,
with_value=True)
if owner != owners.default and \
not ('frozen' in option_bag.properties and 'force_default_on_freeze' in option_bag.properties) and \
not ('frozen' in option_bag.properties and self.force_to_metaconfig(option_bag)):
return value
return self.getdefaultvalue(option_bag)
if owner == owners.default or \
('frozen' in option_bag.properties and \
('force_default_on_freeze' in option_bag.properties or self.force_to_metaconfig(option_bag))):
value = self.getdefaultvalue(option_bag)
else:
value = self.calc_value(option_bag, value)
return value
def calc_value(self, option_bag, value):
if isinstance(value, Calculation):
value = value.execute(option_bag)
elif isinstance(value, (list, tuple)):
value = list(self._do_value_list(value, option_bag))
self.calculate_reset_cache(option_bag, value)
return value
def getdefaultvalue(self,
option_bag):
@ -160,34 +182,36 @@ class Values(object):
# - if option is a submulti, return a list a list
# - if option is a multi, return a list
# - default value
if option_bag.option.impl_is_multi() and option_bag.index is not None:
value = self.calc_value(option_bag, value)
if option_bag.option.impl_is_multi() and option_bag.index is not None and isinstance(value, (list, tuple)):
# if index, must return good value for this index
if len(value) > option_bag.index:
value = value[option_bag.index]
else:
# no value for this index, retrieve default multi value
# default_multi is already a list for submulti
value = option_bag.option.impl_getdefault_multi()
value = self.calc_value(option_bag, option_bag.option.impl_getdefault_multi())
return value
def calculate_reset_cache(self, option_bag, value):
if not 'expire' in option_bag.properties:
return
cache = option_bag.config_bag.context._impl_values_cache
is_cache, cache_value, validated = cache.getcache(option_bag.path,
None,
option_bag.index,
option_bag.config_bag.properties,
option_bag.properties,
'value')
if not is_cache or cache_value == value:
# calculation return same value as previous value,
# so do not invalidate cache
return
# calculated value is a new value, so reset cache
option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
def calculate_value(self,
option_bag: OptionBag) -> Any:
def _reset_cache(_value):
if not 'expire' in option_bag.properties:
return
cache = option_bag.config_bag.context._impl_values_cache
is_cache, cache_value, validated = cache.getcache(option_bag.path,
None,
option_bag.index,
option_bag.config_bag.properties,
option_bag.properties,
'value')
if not is_cache or cache_value == _value:
# calculation return same value as previous value,
# so do not invalidate cache
return
# calculated value is a new value, so reset cache
option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
# if value has callback, calculate value
callback, callback_params = option_bag.option.impl_get_callback()
@ -198,11 +222,11 @@ class Values(object):
# if value is a list and index is set
if option_bag.option.impl_is_submulti() and (value == [] or not isinstance(value[0], list)):
# return value only if it's a submulti and not a list of list
_reset_cache(value)
self.calculate_reset_cache(option_bag, value)
return value
if len(value) > option_bag.index:
# return the value for specified index if found
_reset_cache(value[option_bag.index])
self.calculate_reset_cache(option_bag, value[option_bag.index])
return value[option_bag.index]
# there is no calculate value for this index,
# so return an other default value
@ -222,7 +246,7 @@ class Values(object):
elif option_bag.option.impl_is_multi() and not isinstance(value, list) and option_bag.index is None:
# return a list for a multi
value = [value]
_reset_cache(value)
self.calculate_reset_cache(option_bag, value)
return value
return undefined