de8a8c5e1df4b9360683991b135424fcf38c5138
[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     * if no callback_params:
45       => calculate()
46
47     * if callback_params={'': ('yes',)}
48       => calculate('yes')
49
50     * if callback_params={'value': ('yes',)}
51       => calculate(value='yes')
52
53     * if callback_params={'': ('yes', 'no')}
54       => calculate('yes', 'no')
55
56     * if callback_params={'value': ('yes', 'no')}
57       => ValueError()
58
59     * if callback_params={'': ((opt1, False),)}
60
61        - a simple option:
62          opt1 == 11
63          => calculate(11)
64
65        - a multi option:
66          opt1 == [1, 2, 3]
67          => calculate(1)
68          => calculate(2)
69          => calculate(3)
70
71     * if callback_params={'value': ((opt1, False),)}
72
73        - a simple option:
74          opt1 == 11
75          => calculate(value=11)
76
77        - a multi option:
78          opt1 == [1, 2, 3]
79          => calculate(value=1)
80          => calculate(value=2)
81          => calculate(value=3)
82
83     * if callback_params={'': ((opt1, False), (opt2, False))}
84
85       - a multi option with a simple option
86           opt1 == [1, 2, 3]
87           opt2 == 11
88           => calculate(1, 11)
89           => calculate(2, 11)
90           => calculate(3, 11)
91
92       - a multi option with an other multi option but with same length
93           opt1 == [1, 2, 3]
94           opt2 == [11, 12, 13]
95           callback_params={'': ((opt1, False), (opt2, False))}
96           => calculate(1, 11)
97           => calculate(2, 12)
98           => calculate(3, 13)
99
100       - a multi option with an other multi option but with different length
101           opt1 == [1, 2, 3]
102           opt2 == [11, 12]
103           callback_params={'': ((opt1, False), (opt2, False))}
104           => ConfigError()
105
106     * if callback_params={'value': ((opt1, False), (opt2, False))}
107       => ConfigError()
108
109     If index is not None, return a value, otherwise return:
110
111     * a list if one parameters have multi option
112     * a value otherwise
113
114     If calculate return list, this list is extend to return value.
115     """
116     tcparams = {}
117     one_is_multi = False
118     len_multi = 0
119
120     for key, callbacks in callback_params.items():
121         for callbk in callbacks:
122             if isinstance(callbk, tuple):
123                 option, force_permissive = callbk
124                 # get value
125                 try:
126                     path = config.cfgimpl_get_description().impl_get_path_by_opt(option)
127                     value = config._getattr(path, force_permissive=True)
128                 except PropertiesOptionError as err:
129                     if force_permissive:
130                         continue
131                     raise ConfigError(_('unable to carry out a calculation, '
132                                         'option {0} has properties: {1} for: '
133                                         '{2}').format(option._name, err.proptype,
134                                                       name))
135                 is_multi = option.impl_is_multi()
136                 if is_multi:
137                     if value is not None:
138                         len_value = len(value)
139                         if len_multi != 0 and len_multi != len_value:
140                             raise ConfigError(_('unable to carry out a '
141                                               'calculation, option value with'
142                                               ' multi types must have same '
143                                               'length for: {0}').format(name))
144                         len_multi = len_value
145                     one_is_multi = True
146                 tcparams.setdefault(key, []).append((value, is_multi))
147             else:
148                 tcparams.setdefault(key, []).append((callbk, False))
149
150     if one_is_multi:
151         ret = []
152         if index:
153             if index < len_multi:
154                 range_ = [index]
155             else:
156                 range_ = []
157                 ret = None
158         else:
159             if max_len and max_len < len_multi:
160                 range_ = range(max_len)
161             else:
162                 range_ = range(len_multi)
163         for incr in range_:
164             tcp = {}
165             params = []
166             for key, couples in tcparams.items():
167                 for couple in couples:
168                     value, ismulti = couple
169                     if ismulti and value is not None:
170                         if key == '':
171                             params.append(value[incr])
172                         else:
173                             tcp[key] = value[incr]
174                     else:
175                         params.append(value)
176             calc = calculate(name, callback, params, tcp)
177             if index:
178                 ret = calc
179             else:
180                 if isinstance(calc, list):
181                     ret.extend(calc)
182                 else:
183                     ret.append(calc)
184         return ret
185     else:
186         tcp = {}
187         params = []
188         for key, couples in tcparams.items():
189             for couple in couples:
190                 if key == '':
191                     value = couple[0]
192                     params.append(value)
193                 else:
194                     tcp[key] = couple[0]
195         return calculate(name, callback, params, tcp)
196
197
198 def calculate(name, callback, params, tcparams):
199     """wrapper that launches the 'callback'
200
201     :param callback: callback name
202     :param params: in the callback's arity, the unnamed parameters
203     :param tcparams: in the callback's arity, the named parameters
204
205     """
206     return callback(*params, **tcparams)