add Calculation to values
This commit is contained in:
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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':
|
||||
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user