# Copyright (C) 2012-2013 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 . # # 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" from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.i18n import _ from tiramisu.setting import undefined # ____________________________________________________________ def carry_out_calculation(option, config, callback, callback_params, index=undefined): """a function that carries out a calculation for an option's value :param option: the option :param config: the context config in order to have 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 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(, [], {}) * if callback_params={'': ('yes',)} => calculate(, ['yes'], {}) * if callback_params={'value': ('yes',)} => calculate(, [], {'value': 'yes'}) * if callback_params={'': ('yes', 'no')} => calculate('yes', 'no') * if callback_params={'value': ('yes', 'no')} => ValueError() * if callback_params={'': (['yes', 'no'],)} => calculate(, ['yes', 'no'], {}) * if callback_params={'value': ('yes', 'no')} => raises ValueError() * if callback_params={'': ((opt1, False),)} - a simple option: opt1 == 11 => calculate(, [11], {}) - a multi option and not master/slave: opt1 == [1, 2, 3] => calculate(, [[1, 2, 3]], {}) - option is master or slave of opt1: opt1 == [1, 2, 3] => calculate(, [1], {}) => calculate(, [2], {}) => calculate(, [3], {}) - opt is a master or slave but not related to option: opt1 == [1, 2, 3] => calculate(, [[1, 2, 3]], {}) * if callback_params={'value': ((opt1, False),)} - a simple option: opt1 == 11 => calculate(, [], {'value': 11}) - a multi option: opt1 == [1, 2, 3] => calculate(, [], {'value': [1, 2, 3]}) * if callback_params={'': ((opt1, False), (opt2, False))} - two single options opt1 = 11 opt2 = 12 => calculate(, [11, 12], {}) - a multi option with a simple option opt1 == [1, 2, 3] opt2 == 12 => calculate(, [[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(, [[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(, [[1, 2, 3], [11, 12]], {}) - a multi option without value with a simple option opt1 == [] opt2 == 11 => calculate(, [[], 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. """ tcparams = {} # if callback_params has a callback, launch several time calculate() one_is_multi = False # multi's option should have same value for all option len_multi = None for key, callbacks in callback_params.items(): for callbk in callbacks: if isinstance(callbk, tuple): if len(callbk) == 1: tcparams.setdefault(key, []).append((config, False)) else: # callbk is something link (opt, True|False) opt, force_permissive = callbk path = config.cfgimpl_get_description( ).impl_get_path_by_opt(opt) # get value try: value = config.getattr(path, force_permissive=True, validate=False) # convert to list, not modifie this multi if value.__class__.__name__ == 'Multi': value = list(value) except PropertiesOptionError as err: if force_permissive: continue raise ConfigError(_('unable to carry out a calculation' ', option {0} has properties: {1} ' 'for: {2}').format(opt.impl_getname(), err.proptype, option.impl_getname())) if opt.impl_is_master_slaves() and \ opt.impl_get_master_slaves().in_same_group(option): len_multi = len(value) one_is_multi = True is_multi = True else: is_multi = False tcparams.setdefault(key, []).append((value, is_multi)) else: # callbk is a value and not a multi tcparams.setdefault(key, []).append((callbk, False)) # if one value is a multi, launch several time calculate # if index is set, return a value # if no index, return a list if one_is_multi: ret = [] if index is not undefined: range_ = [index] else: range_ = range(len_multi) for incr in range_: args = [] kwargs = {} for key, couples in tcparams.items(): for couple in couples: value, ismulti = couple if ismulti: val = value[incr] else: val = value if key == '': args.append(val) else: kwargs[key] = val calc = calculate(callback, args, kwargs) if index is not undefined: ret = calc else: ret.append(calc) return ret else: # no value is multi # return a single value args = [] kwargs = {} for key, couples in tcparams.items(): for couple in couples: # couple[1] (ismulti) is always False if key == '': args.append(couple[0]) else: kwargs[key] = couple[0] ret = calculate(callback, args, kwargs) if callback_params != {}: if isinstance(ret, list) and index is not undefined: if len(ret) < index + 1: ret = None else: ret = ret[index] return ret def calculate(callback, args, kwargs): """wrapper that launches the 'callback' :param callback: callback function :param args: in the callback's arity, the unnamed parameters :param kwargs: in the callback's arity, the named parameters """ return callback(*args, **kwargs)