# 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 .error import PropertiesOptionError, ConfigError, ContextError, \ SlaveError from .i18n import _ from .setting import undefined # ____________________________________________________________ def carry_out_calculation(option, context, callback, callback_params, index=undefined): """a function that carries out a calculation for an option's value :param option: the option :param context: 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() master_slave = False # multi's option should have same value for all option if option._is_subdyn(): tcparams['suffix'] = [(option.impl_getsuffix(), False)] for key, callbacks in callback_params.items(): for callbk in callbacks: if isinstance(callbk, tuple): if context is undefined: raise ContextError() # pragma: optional cover if callbk[0] is None: # pragma: optional cover #Not an option, set full context tcparams.setdefault(key, []).append((context, False)) else: # callbk is something link (opt, True|False) opt, force_permissive = callbk if opt._is_subdyn(): root = '.'.join(option.impl_getpath(context).split('.')[:-1]) name = opt.impl_getname() + option.impl_getsuffix() path = root + '.' + name opt = opt._impl_to_dyn(name, path) else: path = context.cfgimpl_get_description( ).impl_get_path_by_opt(opt) # get value try: value = context.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: # pragma: optional cover 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): master_slave = 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 master_slave: ret = [] args = [] kwargs = {} for key, couples in tcparams.items(): for couple in couples: value, ismulti = couple if ismulti: val = value[index] else: val = value if key == '': args.append(val) else: kwargs[key] = val return calculate(callback, args, kwargs) 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) try: if callback_params != {} and isinstance(ret, list) and \ option.impl_is_master_slaves('slave'): raise SlaveError(_("callback cannot return a list for a " "slave option ({0})").format(path)) except AttributeError: pass 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)