tiramisu/tiramisu/autolib.py

298 lines
11 KiB
Python
Raw Normal View History

2018-01-26 07:33:47 +01:00
# Copyright (C) 2012-2018 Team tiramisu (see AUTHORS for all contributors)
#
2013-09-22 22:33:09 +02:00
# 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.
#
2013-09-22 22:33:09 +02:00
# 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.
#
2013-09-22 22:33:09 +02:00
# 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/>.
#
2012-09-18 09:48:41 +02:00
# The original `Config` design model is unproudly borrowed from
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
"enables us to carry out a calculation and return an option's value"
2017-11-28 22:42:30 +01:00
from .error import PropertiesOptionError, ConfigError, SlaveError, display_list
from .i18n import _
from .setting import undefined
2017-12-07 22:20:19 +01:00
from .option.symlinkoption import DynSymLinkOption
2017-07-16 10:57:43 +02:00
from .storage import get_default_values_storages, get_default_settings_storages
from .function import ParamValue, ParamContext, ParamIndex
# ____________________________________________________________
2013-04-03 12:20:26 +02:00
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:
2018-04-25 19:12:54 +02:00
sconfig_bag.setting_properties = None
sconfig_bag.force_unrestraint= False
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))
2017-11-28 22:42:30 +01:00
def carry_out_calculation(option,
context,
callback,
callback_params,
2017-12-19 23:11:45 +01:00
index,
config_bag,
2017-12-27 15:48:49 +01:00
orig_value=undefined,
2017-11-28 22:42:30 +01:00
is_validator=False):
2013-08-21 14:52:48 +02:00
"""a function that carries out a calculation for an option's value
2013-04-03 12:20:26 +02:00
2014-01-30 22:55:15 +01:00
:param option: the option
2015-04-18 22:53:45 +02:00
:param context: the context config in order to have
2013-08-21 14:52:48 +02:00
the whole options available
:param callback: the name of the callback function
:type callback: str
:param callback_params: the callback's parameters
(only keyword parameters are allowed)
:type callback_params: dict
:param index: if an option is multi, only calculates the nth value
:type index: int
:param is_validator: to know if carry_out_calculation is used to validate a value
The callback_params is a dict. Key is used to build args (if key is '')
and kwargs (otherwise). Values are tuple of:
- values
- tuple with option and boolean's force_permissive (True when don't raise
if PropertiesOptionError)
Values could have multiple values only when key is ''.
* if no callback_params:
=> calculate(<function func at 0x2092320>, [], {})
* if callback_params={'': ('yes',)}
=> calculate(<function func at 0x2092320>, ['yes'], {})
* if callback_params={'value': ('yes',)}
=> calculate(<function func at 0x165b320>, [], {'value': 'yes'})
* if callback_params={'': ('yes', 'no')}
=> calculate('yes', 'no')
* if callback_params={'value': ('yes', 'no')}
=> ValueError()
* if callback_params={'': (['yes', 'no'],)}
=> calculate(<function func at 0x176b320>, ['yes', 'no'], {})
* if callback_params={'value': ('yes', 'no')}
=> raises ValueError()
* if callback_params={'': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(<function func at 0x1cea320>, [11], {})
- a multi option and not master/slave:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x223c320>, [[1, 2, 3]], {})
- option is master or slave of opt1:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x223c320>, [1], {})
=> calculate(<function func at 0x223c320>, [2], {})
=> calculate(<function func at 0x223c320>, [3], {})
- opt is a master or slave but not related to option:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x11b0320>, [[1, 2, 3]], {})
* if callback_params={'value': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(<function func at 0x17ff320>, [], {'value': 11})
- a multi option:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x1262320>, [], {'value': [1, 2, 3]})
* if callback_params={'': ((opt1, False), (opt2, False))}
- two single options
opt1 = 11
opt2 = 12
=> calculate(<function func at 0x217a320>, [11, 12], {})
- a multi option with a simple option
opt1 == [1, 2, 3]
opt2 == 12
=> calculate(<function func at 0x2153320>, [[1, 2, 3], 12], {})
- a multi option with an other multi option but with same length
opt1 == [1, 2, 3]
opt2 == [11, 12, 13]
=> calculate(<function func at 0x1981320>, [[1, 2, 3], [11, 12, 13]], {})
- a multi option with an other multi option but with different length
opt1 == [1, 2, 3]
opt2 == [11, 12]
=> calculate(<function func at 0x2384320>, [[1, 2, 3], [11, 12]], {})
- a multi option without value with a simple option
opt1 == []
opt2 == 11
=> calculate(<function func at 0xb65320>, [[], 12], {})
* if callback_params={'value': ((opt1, False), (opt2, False))}
=> raises ValueError()
If index is not undefined, return a value, otherwise return:
* a list if one parameters have multi option
* a value otherwise
If calculate return list, this list is extend to return value.
2013-08-21 14:52:48 +02:00
"""
2017-12-19 23:11:45 +01:00
args = []
kwargs = {}
# if callback_params has a callback, launch several time calculate()
2018-04-09 21:37:49 +02:00
if option.issubdyn():
#FIXME why here? should be ParamSuffix !
2017-12-19 23:11:45 +01:00
kwargs['suffix'] = option.impl_getsuffix()
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
2017-12-19 23:11:45 +01:00
args.append(value)
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
2017-12-19 23:11:45 +01:00
kwargs[key] = value
except PropertiesOptionError:
pass
2017-12-19 23:11:45 +01:00
ret = calculate(option,
callback,
is_validator,
args,
kwargs)
if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \
option.impl_is_master_slaves('slave'):
if args or kwargs:
raise SlaveError(_('function "{}" with arguments "{}" and "{}" '
'return the list "{}" for the slave option "{}"'
'').format(callback.__name__,
args,
kwargs,
2018-04-09 21:37:49 +02:00
ret,
2017-12-19 23:11:45 +01:00
option.impl_get_display_name()))
2018-04-09 21:37:49 +02:00
else:
raise SlaveError(_('function "{}" return the list "{}" for the slave option "{}"'
'').format(callback.__name__,
ret,
option.impl_getname()))
2017-12-19 23:11:45 +01:00
return ret
2012-09-18 09:48:41 +02:00
2013-04-03 12:20:26 +02:00
def calculate(option, callback, is_validator, args, kwargs):
2013-05-23 14:55:52 +02:00
"""wrapper that launches the 'callback'
2013-05-10 16:02:27 +02:00
:param callback: callback function
:param args: in the callback's arity, the unnamed parameters
:param kwargs: in the callback's arity, the named parameters
2013-05-23 14:55:52 +02:00
2013-05-10 16:02:27 +02:00
"""
2016-10-14 22:20:14 +02:00
try:
return callback(*args, **kwargs)
2016-10-14 22:20:14 +02:00
except ValueError as err:
if is_validator:
2017-11-28 22:42:30 +01:00
raise err
error = err
except Exception as err:
error = err
2017-12-19 23:11:45 +01:00
if args or kwargs:
msg = _('unexpected error "{0}" in function "{1}" with arguments "{3}" and "{4}" '
'for option "{2}"').format(str(error),
2017-07-09 09:49:03 +02:00
callback.__name__,
option.impl_get_display_name(),
args,
kwargs)
else:
msg = _('unexpected error "{0}" in function "{1}" for option "{2}"'
'').format(str(error),
2017-07-09 09:49:03 +02:00
callback.__name__,
option.impl_get_display_name())
raise ConfigError(msg)