comment tiramisu/autolib.py + some modification
[tiramisu.git] / tiramisu / autolib.py
1 # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 #
17 # The original `Config` design model is unproudly borrowed from
18 # the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
19 # the whole pypy projet is under MIT licence
20 # ____________________________________________________________
21 "enables us to carry out a calculation and return an option's value"
22 from tiramisu.error import PropertiesOptionError, ConfigError
23 from tiramisu.i18n import _
24 # ____________________________________________________________
25
26
27 def carry_out_calculation(name, config, callback, callback_params,
28                           index=None, max_len=None):
29     """a function that carries out a calculation for an option's value
30
31     :param name: the option name (`opt._name`)
32     :param config: the context config in order to have
33                    the whole options available
34     :param callback: the name of the callback function
35     :type callback: str
36     :param callback_params: the callback's parameters
37                             (only keyword parameters are allowed)
38     :type callback_params: dict
39     :param index: if an option is multi, only calculates the nth value
40     :type index: int
41     :param max_len: max length for a multi
42     :type max_len: int
43
44     The callback_params is a dict. Key is used to build args (if key is '')
45     and kwargs (otherwise). Values are tuple of:
46     - values
47     - tuple with option and boolean's force_permissive (True when don't raise
48     if PropertiesOptionError)
49     Values could have multiple values only when key is ''.
50
51     * if no callback_params:
52       => calculate()
53
54     * if callback_params={'': ('yes',)}
55       => calculate('yes')
56
57     * if callback_params={'value': ('yes',)}
58       => calculate(value='yes')
59
60     * if callback_params={'': ('yes', 'no')}
61       => calculate('yes', 'no')
62
63     * if callback_params={'value': ('yes', 'no')}
64       => ValueError()
65
66     * if callback_params={'': ((opt1, False),)}
67
68        - a simple option:
69          opt1 == 11
70          => calculate(11)
71
72        - a multi option:
73          opt1 == [1, 2, 3]
74          => calculate(1)
75          => calculate(2)
76          => calculate(3)
77
78     * if callback_params={'value': ((opt1, False),)}
79
80        - a simple option:
81          opt1 == 11
82          => calculate(value=11)
83
84        - a multi option:
85          opt1 == [1, 2, 3]
86          => calculate(value=1)
87          => calculate(value=2)
88          => calculate(value=3)
89
90     * if callback_params={'': ((opt1, False), (opt2, False))}
91
92       - a multi option with a simple option
93           opt1 == [1, 2, 3]
94           opt2 == 11
95           => calculate(1, 11)
96           => calculate(2, 11)
97           => calculate(3, 11)
98
99       - a multi option with an other multi option but with same length
100           opt1 == [1, 2, 3]
101           opt2 == [11, 12, 13]
102           => calculate(1, 11)
103           => calculate(2, 12)
104           => calculate(3, 13)
105
106       - a multi option with an other multi option but with different length
107           opt1 == [1, 2, 3]
108           opt2 == [11, 12]
109           => ConfigError()
110
111       - a multi option without value with a simple option
112           opt1 == []
113           opt2 == 11
114           => []
115
116     * if callback_params={'value': ((opt1, False), (opt2, False))}
117       => ConfigError()
118
119     If index is not None, return a value, otherwise return:
120
121     * a list if one parameters have multi option
122     * a value otherwise
123
124     If calculate return list, this list is extend to return value.
125     """
126     tcparams = {}
127     # if callback_params has a callback, launch several time calculate()
128     one_is_multi = False
129     # multi's option should have same value for all option
130     len_multi = None
131
132     for key, callbacks in callback_params.items():
133         for callbk in callbacks:
134             if isinstance(callbk, tuple):
135                 # callbk is something link (opt, True|False)
136                 option, force_permissive = callbk
137                 path = config.cfgimpl_get_description().impl_get_path_by_opt(
138                     option)
139                 # get value
140                 try:
141                     value = config._getattr(path, force_permissive=True)
142                 except PropertiesOptionError as err:
143                     if force_permissive:
144                         continue
145                     raise ConfigError(_('unable to carry out a calculation, '
146                                         'option {0} has properties: {1} for: '
147                                         '{2}').format(option._name,
148                                                       err.proptype,
149                                                       name))
150                 is_multi = option.impl_is_multi()
151                 if is_multi:
152                     len_value = len(value)
153                     if len_multi is not None and len_multi != len_value:
154                         raise ConfigError(_('unable to carry out a '
155                                             'calculation, option value with'
156                                             ' multi types must have same '
157                                             'length for: {0}').format(name))
158                     len_multi = len_value
159                     one_is_multi = True
160                 tcparams.setdefault(key, []).append((value, is_multi))
161             else:
162                 # callbk is a value and not a multi
163                 tcparams.setdefault(key, []).append((callbk, False))
164
165     # if one value is a multi, launch several time calculate
166     # if index is set, return a value
167     # if no index, return a list
168     if one_is_multi:
169         ret = []
170         if index:
171             if index < len_multi:
172                 range_ = [index]
173             else:
174                 range_ = []
175                 ret = None
176         else:
177             if max_len and max_len < len_multi:
178                 range_ = range(max_len)
179             else:
180                 range_ = range(len_multi)
181         for incr in range_:
182             args = []
183             kwargs = {}
184             for key, couples in tcparams.items():
185                 for couple in couples:
186                     value, ismulti = couple
187                     if ismulti:
188                         val = value[incr]
189                     else:
190                         val = value
191                     if key == '':
192                         args.append(val)
193                     else:
194                         kwargs[key] = val
195             calc = calculate(callback, args, kwargs)
196             if index:
197                 ret = calc
198             else:
199                 if isinstance(calc, list):
200                     ret.extend(calc)
201                 else:
202                     ret.append(calc)
203         return ret
204     else:
205         # no value is multi
206         # return a single value
207         args = []
208         kwargs = {}
209         for key, couples in tcparams.items():
210             for couple in couples:
211                 # couple[1] (ismulti) is always False
212                 if key == '':
213                     args.append(couple[0])
214                 else:
215                     kwargs[key] = couple[0]
216         return calculate(callback, args, kwargs)
217
218
219 def calculate(callback, args, kwargs):
220     """wrapper that launches the 'callback'
221
222     :param callback: callback function
223     :param args: in the callback's arity, the unnamed parameters
224     :param kwargs: in the callback's arity, the named parameters
225
226     """
227     return callback(*args, **kwargs)