remove an unused callback in test
[tiramisu.git] / tiramisu / value.py
1 # -*- coding: utf-8 -*-
2 "takes care of the option's values and multi values"
3 # Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 #
19 # ____________________________________________________________
20 from time import time
21 from tiramisu.error import ConfigError, SlaveError
22 from tiramisu.setting import owners, multitypes, expires_time
23 from tiramisu.autolib import carry_out_calculation
24 from tiramisu.i18n import _
25
26
27 class Values(object):
28     __slots__ = ('context', '_values', '_cache')
29
30     def __init__(self, context):
31         """
32         Initializes the values's dict.
33
34         :param context: the context is the home config's values
35         """
36         "Config's root indeed is in charge of the `Option()`'s values"
37         self.context = context
38         self._values = {}
39         self._cache = {}
40         super(Values, self).__init__()
41
42     def _get_value(self, opt):
43         "return value or default value if not set"
44         #if no value
45         if opt not in self._values:
46             value = opt.optimpl_getdefault()
47             if opt.optimpl_is_multi():
48                 value = Multi(value, self.context, opt)
49         else:
50             #if value
51             value = self._values[opt][1]
52         return value
53
54     def __delitem__(self, opt):
55         self._reset(opt)
56
57     def _reset(self, opt):
58         if opt in self._values:
59             self.context.cfgimpl_reset_cache()
60             del(self._values[opt])
61
62     def _is_empty(self, opt, value):
63         "convenience method to know if an option is empty"
64         empty = opt._empty
65         if (not opt.optimpl_is_multi() and (value is None or value == empty)) or \
66            (opt.optimpl_is_multi() and (value == [] or
67                                         None in value or empty in value)):
68             return True
69         return False
70
71     def _getcallback_value(self, opt):
72         callback, callback_params = opt._callback
73         if callback_params is None:
74             callback_params = {}
75         return carry_out_calculation(opt._name, config=self.context,
76                                      callback=callback,
77                                      callback_params=callback_params)
78
79     def __getitem__(self, opt):
80         return self.getitem(opt)
81
82     def get_modified_values(self):
83         return self._values
84
85     def getitem(self, opt, validate=True, force_permissive=False,
86                 force_properties=None):
87         if opt in self._cache:
88             exp = time()
89             value, created = self._cache[opt]
90             if exp < created:
91                 return value
92         val = self._getitem(opt, validate, force_permissive, force_properties)
93         self._set_cache(opt, val)
94         return val
95
96     def _getitem(self, opt, validate, force_permissive, force_properties):
97         # options with callbacks
98         setting = self.context.cfgimpl_get_settings()
99         value = self._get_value(opt)
100         is_frozen = 'frozen' in setting[opt]
101         if opt.optimpl_has_callback():
102             #if value is set and :
103             # - not frozen
104             # - frozen and not force_default_on_freeze
105             if not self.is_default_owner(opt) and (
106                     not is_frozen or (is_frozen and
107                                       not 'force_default_on_freeze' in setting[opt])):
108                 pass
109             else:
110                 value = self._getcallback_value(opt)
111                 if opt.optimpl_is_multi():
112                     value = Multi(value, self.context, opt)
113                 #suppress value if already set
114                 self._reset(opt)
115         # frozen and force default
116         elif is_frozen and 'force_default_on_freeze' in setting[opt]:
117             value = opt.optimpl_getdefault()
118             if opt.optimpl_is_multi():
119                 value = Multi(value, self.context, opt)
120         if validate and not opt.optimpl_validate(value, self.context, 'validator' in setting):
121             raise ValueError(_('invalid calculated value returned'
122                              ' for option {0}: {1}').format(opt._name, value))
123         if self.is_default_owner(opt) and \
124                 'force_store_value' in setting[opt]:
125             self.setitem(opt, value)
126         setting.validate_properties(opt, False, False, value=value,
127                                     force_permissive=force_permissive,
128                                     force_properties=force_properties)
129         return value
130
131     def __setitem__(self, opt, value):
132         self.setitem(opt, value)
133
134     def setitem(self, opt, value, force_permissive=False):
135         self._setitem(opt, value, force_permissive)
136
137     def _setitem(self, opt, value, force_permissive=False, force_properties=None):
138         #valid opt
139         if not opt.optimpl_validate(value, self.context,
140                                     'validator' in self.context.cfgimpl_get_settings()):
141             raise ValueError(_('invalid value {}'
142                              ' for option {}').format(value, opt._name))
143         if opt.optimpl_is_multi() and not isinstance(value, Multi):
144             value = Multi(value, self.context, opt)
145         self._setvalue(opt, value, force_permissive=force_permissive,
146                        force_properties=force_properties)
147
148     def _setvalue(self, opt, value, force_permissive=False, force_properties=None):
149         self.context.cfgimpl_reset_cache()
150         setting = self.context.cfgimpl_get_settings()
151         setting.validate_properties(opt, False, True,
152                                     value=value,
153                                     force_permissive=force_permissive,
154                                     force_properties=force_properties)
155         self._values[opt] = (setting.getowner(), value)
156
157     def getowner(self, opt):
158         return self._values.get(opt, (owners.default, None))[0]
159
160     def setowner(self, opt, owner):
161         if opt not in self._values:
162             raise ConfigError(_('no value for {1} cannot change owner to {2}').format(opt))
163         if not isinstance(owner, owners.Owner):
164             raise TypeError(_("invalid generic owner {0}").format(str(owner)))
165         self._values[opt] = (owner, self._values[opt][1])
166
167     def is_default_owner(self, opt):
168         """
169         :param config: *must* be only the **parent** config
170                        (not the toplevel config)
171         :return: boolean
172         """
173         return self.getowner(opt) == owners.default
174
175     def _set_cache(self, opt, val):
176         if 'expire' in self.context.cfgimpl_get_settings():
177             self._cache[opt] = (val, time() + expires_time)
178
179     def reset_cache(self, only_expired):
180         if only_expired:
181             exp = time()
182             keys = self._cache.keys()
183             for key in keys:
184                 val, created = self._cache[key]
185                 if exp > created:
186                     del(self._cache[key])
187         else:
188             self._cache.clear()
189
190     def __contains__(self, opt):
191         return opt in self._values
192
193 # ____________________________________________________________
194 # multi types
195
196
197 class Multi(list):
198     """multi options values container
199     that support item notation for the values of multi options"""
200     __slots__ = ('opt', 'context')
201
202     def __init__(self, value, context, opt):
203         """
204         :param value: the Multi wraps a list value
205         :param context: the home config that has the values
206         :param opt: the option object that have this Multi value
207         """
208         self.opt = opt
209         self.context = context
210         if not isinstance(value, list):
211             value = [value]
212         if self.opt.optimpl_get_multitype() == multitypes.slave:
213             value = self._valid_slave(value)
214         elif self.opt.optimpl_get_multitype() == multitypes.master:
215             self._valid_master(value)
216         super(Multi, self).__init__(value)
217
218     def _valid_slave(self, value):
219         #if slave, had values until master's one
220         masterp = self.context.cfgimpl_get_description().optimpl_get_path_by_opt(
221             self.opt.optimpl_get_master_slaves())
222         mastervalue = getattr(self.context, masterp)
223         masterlen = len(mastervalue)
224         if len(value) > masterlen or (len(value) < masterlen and
225                                       not self.context.cfgimpl_get_values().is_default_owner(self.opt)):
226             raise SlaveError(_("invalid len for the slave: {0}"
227                                " which has {1} as master").format(
228                                    self.opt._name, masterp))
229         elif len(value) < masterlen:
230             for num in range(0, masterlen - len(value)):
231                 value.append(self.opt.optimpl_getdefault_multi())
232         #else: same len so do nothing
233         return value
234
235     def _valid_master(self, value):
236         masterlen = len(value)
237         values = self.context.cfgimpl_get_values()
238         for slave in self.opt._master_slaves:
239             if not values.is_default_owner(slave):
240                 value_slave = values._get_value(slave)
241                 if len(value_slave) > masterlen:
242                     raise SlaveError(_("invalid len for the master: {0}"
243                                        " which has {1} as slave with"
244                                        " greater len").format(
245                                            self.opt._name, slave._name))
246                 elif len(value_slave) < masterlen:
247                     for num in range(0, masterlen - len(value_slave)):
248                         value_slave.append(slave.optimpl_getdefault_multi(), force=True)
249
250     def __setitem__(self, key, value):
251         self._validate(value)
252         #assume not checking mandatory property
253         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
254         super(Multi, self).__setitem__(key, value)
255
256     def append(self, value, force=False):
257         """the list value can be updated (appened)
258         only if the option is a master
259         """
260         if not force:
261             if self.opt.optimpl_get_multitype() == multitypes.slave:
262                 raise SlaveError(_("cannot append a value on a multi option {0}"
263                                    " which is a slave").format(self.opt._name))
264             elif self.opt.optimpl_get_multitype() == multitypes.master:
265                 for slave in self.opt.optimpl_get_master_slaves():
266                     values = self.context.cfgimpl_get_values()
267                     if not values.is_default_owner(slave):
268                         values[slave].append(slave.optimpl_getdefault_multi(),
269                                              force=True)
270         self._validate(value)
271         #assume not checking mandatory property
272         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
273         super(Multi, self).append(value)
274
275     def _validate(self, value):
276         if value is not None and not self.opt._validate(value):
277             raise ValueError(_("invalid value {0} "
278                              "for option {1}").format(str(value),
279                                                       self.opt._name))
280
281     def pop(self, key, force=False):
282         """the list value can be updated (poped)
283         only if the option is a master
284
285         :param key: index of the element to pop
286         :return: the requested element
287         """
288         if not force:
289             if self.opt.optimpl_get_multitype() == multitypes.slave:
290                 raise SlaveError(_("cannot pop a value on a multi option {0}"
291                                    " which is a slave").format(self.opt._name))
292             elif self.opt.optimpl_get_multitype() == multitypes.master:
293                 for slave in self.opt.optimpl_get_master_slaves():
294                     self.context.cfgimpl_get_values()[slave].pop(key, force=True)
295         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
296         return super(Multi, self).pop(key)