add sqlite plugin
[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 copy import copy
22 from tiramisu.error import ConfigError, SlaveError
23 from tiramisu.setting import owners, multitypes, expires_time
24 from tiramisu.autolib import carry_out_calculation
25 from tiramisu.i18n import _
26 from tiramisu.option import SymLinkOption
27
28 #FIXME
29 #from tiramisu.plugins.dictionary.value import PluginValues
30 from tiramisu.plugins.sqlite3.value import PluginValues
31
32
33 class Values(PluginValues):
34     """The `Config`'s root is indeed  in charge of the `Option()`'s values,
35     but the values are physicaly located here, in `Values`, wich is also
36     responsible of a caching utility.
37     """
38     __slots__ = ('context',)
39
40     def __init__(self, context, config_id):
41         """
42         Initializes the values's dict.
43
44         :param context: the context is the home config's values
45
46         """
47
48         self.context = context
49         super(Values, self).__init__(config_id)
50
51     def _getdefault(self, opt):
52         meta = self.context.cfgimpl_get_meta()
53         if meta is not None:
54             value = meta.cfgimpl_get_values()[opt]
55         else:
56             value = opt.impl_getdefault()
57         if opt.impl_is_multi():
58             return copy(value)
59         else:
60             return value
61
62     def _getvalue(self, opt, validate=True):
63         "return value or default value if not set"
64         if not self._p_hasvalue(opt):
65             #if no value
66             value = self._getdefault(opt)
67             if opt.impl_is_multi():
68                 value = Multi(value, self.context, opt, validate)
69         else:
70             #if value
71             value = self._p_getvalue(opt)
72             if opt.impl_is_multi() and not isinstance(value, Multi):
73                 #load value so don't need to validate if is not a Multi
74                 value = Multi(value, self.context, opt, validate=False)
75         return value
76
77     def get_modified_values(self):
78         return self._p_get_modified_values()
79
80     def __contains__(self, opt):
81         return self._p_hasvalue('value', opt)
82
83     def __delitem__(self, opt):
84         self.reset(opt)
85
86     def reset(self, opt):
87         if self._p_hasvalue(opt):
88             setting = self.context.cfgimpl_get_settings()
89             opt.impl_validate(opt.impl_getdefault(), self.context,
90                               'validator' in setting)
91             self.context.cfgimpl_reset_cache()
92             if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master:
93                 for slave in opt.impl_get_master_slaves():
94                     self.reset(slave)
95             self._p_resetvalue(opt)
96
97     def _isempty(self, opt, value):
98         "convenience method to know if an option is empty"
99         empty = opt._empty
100         if (not opt.impl_is_multi() and (value is None or value == empty)) or \
101            (opt.impl_is_multi() and (value == [] or
102                                      None in value or empty in value)):
103             return True
104         return False
105
106     def _getcallback_value(self, opt, index=None):
107         callback, callback_params = opt._callback
108         if callback_params is None:
109             callback_params = {}
110         return carry_out_calculation(opt._name, config=self.context,
111                                      callback=callback,
112                                      callback_params=callback_params,
113                                      index=index)
114
115     def __getitem__(self, opt):
116         return self.getitem(opt)
117
118     def getitem(self, opt, validate=True, force_permissive=False,
119                 force_properties=None, validate_properties=True):
120         ntime = None
121         if self._p_hascache('value', opt):
122             ntime = time()
123             is_cached, value = self._p_getcache('value', opt, ntime)
124             if is_cached:
125                 if opt.impl_is_multi() and not isinstance(value, Multi):
126                     #load value so don't need to validate if is not a Multi
127                     value = Multi(value, self.context, opt, validate=False)
128                 return value
129         val = self._getitem(opt, validate, force_permissive, force_properties,
130                             validate_properties)
131         if 'expire' in self.context.cfgimpl_get_settings() and validate and \
132                 validate_properties and force_permissive is False and \
133                 force_properties is None:
134             if ntime is None:
135                 ntime = time()
136             self._p_setcache('value', opt, val, ntime + expires_time)
137
138         return val
139
140     def _getitem(self, opt, validate, force_permissive, force_properties,
141                  validate_properties):
142         # options with callbacks
143         setting = self.context.cfgimpl_get_settings()
144         is_frozen = 'frozen' in setting[opt]
145         #if value is callback and is not set or frozen with force_default_on_freeze
146         if opt.impl_has_callback() and (self.is_default_owner(opt) or
147                                         (is_frozen and
148                                          'force_default_on_freeze' in setting[opt])):
149             no_value_slave = False
150             if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave:
151                 masterp = self.context.cfgimpl_get_description().impl_get_path_by_opt(
152                     opt.impl_get_master_slaves())
153                 mastervalue = getattr(self.context, masterp)
154                 lenmaster = len(mastervalue)
155                 if lenmaster == 0:
156                     value = []
157                     no_value_slave = True
158
159             if not no_value_slave:
160                 value = self._getcallback_value(opt)
161                 if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave:
162                     if not isinstance(value, list):
163                         value = [value for i in range(lenmaster)]
164             if opt.impl_is_multi():
165                 value = Multi(value, self.context, opt, validate)
166             #suppress value if already set
167             self.reset(opt)
168         # frozen and force default
169         elif is_frozen and 'force_default_on_freeze' in setting[opt]:
170             value = self._getdefault(opt)
171             if opt.impl_is_multi():
172                 value = Multi(value, self.context, opt, validate)
173         else:
174             value = self._getvalue(opt, validate)
175         if validate:
176             opt.impl_validate(value, self.context, 'validator' in setting)
177         if self.is_default_owner(opt) and \
178                 'force_store_value' in setting[opt]:
179             self.setitem(opt, value, is_write=False)
180         if validate_properties:
181             setting.validate_properties(opt, False, False, value=value,
182                                         force_permissive=force_permissive,
183                                         force_properties=force_properties)
184         return value
185
186     def __setitem__(self, opt, value):
187         self.setitem(opt, value)
188
189     def setitem(self, opt, value, force_permissive=False, is_write=True):
190         #is_write is, for example, used with "force_store_value"
191         #user didn't change value, so not write
192         #valid opt
193         opt.impl_validate(value, self.context,
194                           'validator' in self.context.cfgimpl_get_settings())
195         if opt.impl_is_multi() and not isinstance(value, Multi):
196             value = Multi(value, self.context, opt)
197         self._setvalue(opt, value, force_permissive=force_permissive,
198                        is_write=is_write)
199
200     def _setvalue(self, opt, value, force_permissive=False, force_properties=None,
201                   is_write=True, validate_properties=True):
202         self.context.cfgimpl_reset_cache()
203         if validate_properties:
204             setting = self.context.cfgimpl_get_settings()
205             setting.validate_properties(opt, False, is_write,
206                                         value=value,
207                                         force_permissive=force_permissive,
208                                         force_properties=force_properties)
209         self._p_setvalue(opt, value)
210
211     def getowner(self, opt):
212         if isinstance(opt, SymLinkOption):
213             opt = opt._opt
214         owner = self._p_getowner(opt, owners.default)
215         meta = self.context.cfgimpl_get_meta()
216         if owner is owners.default and meta is not None:
217             owner = meta.cfgimpl_get_values().getowner(opt)
218         return owner
219
220     def setowner(self, opt, owner):
221         if not isinstance(owner, owners.Owner):
222             raise TypeError(_("invalid generic owner {0}").format(str(owner)))
223         if self.getowner(opt) == owners.default:
224             raise ConfigError(_('no value for {0} cannot change owner to {1}'
225                                 '').format(opt._name, owner))
226         self._p_setowner(opt, owner)
227
228     def is_default_owner(self, opt):
229         """
230         :param config: *must* be only the **parent** config
231                        (not the toplevel config)
232         :return: boolean
233         """
234         return self.getowner(opt) == owners.default
235
236     def reset_cache(self, only_expired):
237         if only_expired:
238             self._p_reset_expired_cache('value', time())
239         else:
240             self._p_reset_all_cache('value')
241
242     def _get_opt_path(self, opt):
243         return self.context.cfgimpl_get_description().impl_get_path_by_opt(opt)
244
245     def _get_owner_from_str(self, owner):
246         return getattr(owners, owner)
247
248 # ____________________________________________________________
249 # multi types
250
251
252 class Multi(list):
253     """multi options values container
254     that support item notation for the values of multi options"""
255     __slots__ = ('opt', 'context')
256
257     def __init__(self, value, context, opt, validate=True):
258         """
259         :param value: the Multi wraps a list value
260         :param context: the home config that has the values
261         :param opt: the option object that have this Multi value
262         """
263         self.opt = opt
264         self.context = context
265         if not isinstance(value, list):
266             value = [value]
267         if validate and self.opt.impl_get_multitype() == multitypes.slave:
268             value = self._valid_slave(value)
269         elif self.opt.impl_get_multitype() == multitypes.master:
270             self._valid_master(value)
271         super(Multi, self).__init__(value)
272
273     def _valid_slave(self, value):
274         #if slave, had values until master's one
275         masterp = self.context.cfgimpl_get_description().impl_get_path_by_opt(
276             self.opt.impl_get_master_slaves())
277         mastervalue = getattr(self.context, masterp)
278         masterlen = len(mastervalue)
279         if len(value) > masterlen or (len(value) < masterlen and
280                                       not self.context.cfgimpl_get_values().is_default_owner(self.opt)):
281             raise SlaveError(_("invalid len for the slave: {0}"
282                                " which has {1} as master").format(
283                                    self.opt._name, masterp))
284         elif len(value) < masterlen:
285             for num in range(0, masterlen - len(value)):
286                 value.append(self.opt.impl_getdefault_multi())
287         #else: same len so do nothing
288         return value
289
290     def _valid_master(self, value):
291         masterlen = len(value)
292         values = self.context.cfgimpl_get_values()
293         for slave in self.opt._master_slaves:
294             if not values.is_default_owner(slave):
295                 value_slave = values._getvalue(slave)
296                 if len(value_slave) > masterlen:
297                     raise SlaveError(_("invalid len for the master: {0}"
298                                        " which has {1} as slave with"
299                                        " greater len").format(
300                                            self.opt._name, slave._name))
301                 elif len(value_slave) < masterlen:
302                     for num in range(0, masterlen - len(value_slave)):
303                         value_slave.append(slave.impl_getdefault_multi(), force=True)
304
305     def __setitem__(self, key, value):
306         self._validate(value)
307         #assume not checking mandatory property
308         super(Multi, self).__setitem__(key, value)
309         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
310
311     def append(self, value, force=False):
312         """the list value can be updated (appened)
313         only if the option is a master
314         """
315         if not force:
316             if self.opt.impl_get_multitype() == multitypes.slave:
317                 raise SlaveError(_("cannot append a value on a multi option {0}"
318                                    " which is a slave").format(self.opt._name))
319             elif self.opt.impl_get_multitype() == multitypes.master:
320                 values = self.context.cfgimpl_get_values()
321                 if value is None and self.opt.impl_has_callback():
322                     value = values._getcallback_value(self.opt)
323                     #Force None il return a list
324                     if isinstance(value, list):
325                         value = None
326         self._validate(value)
327         super(Multi, self).append(value)
328         self.context.cfgimpl_get_values()._setvalue(self.opt, self, validate_properties=not force)
329         if not force and self.opt.impl_get_multitype() == multitypes.master:
330             for slave in self.opt.impl_get_master_slaves():
331                 if not values.is_default_owner(slave):
332                     if slave.impl_has_callback():
333                         index = self.__len__() - 1
334                         dvalue = values._getcallback_value(slave, index=index)
335                     else:
336                         dvalue = slave.impl_getdefault_multi()
337                     old_value = values.getitem(slave, validate_properties=False)
338                     if len(old_value) < self.__len__():
339                         values.getitem(slave, validate_properties=False).append(
340                             dvalue, force=True)
341                     else:
342                         values.getitem(slave, validate_properties=False)[index] = dvalue
343
344     def sort(self, cmp=None, key=None, reverse=False):
345         if self.opt.impl_get_multitype() in [multitypes.slave,
346                                              multitypes.master]:
347             raise SlaveError(_("cannot sort multi option {0} if master or slave"
348                                "").format(self.opt._name))
349         super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse)
350         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
351
352     def reverse(self):
353         if self.opt.impl_get_multitype() in [multitypes.slave,
354                                              multitypes.master]:
355             raise SlaveError(_("cannot reverse multi option {0} if master or "
356                                "slave").format(self.opt._name))
357         super(Multi, self).reverse()
358         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
359
360     def insert(self, index, obj):
361         if self.opt.impl_get_multitype() in [multitypes.slave,
362                                              multitypes.master]:
363             raise SlaveError(_("cannot insert multi option {0} if master or "
364                                "slave").format(self.opt._name))
365         super(Multi, self).insert(index, obj)
366         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
367
368     def extend(self, iterable):
369         if self.opt.impl_get_multitype() in [multitypes.slave,
370                                              multitypes.master]:
371             raise SlaveError(_("cannot extend multi option {0} if master or "
372                                "slave").format(self.opt._name))
373         super(Multi, self).extend(iterable)
374         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
375
376     def _validate(self, value):
377         if value is not None:
378             try:
379                 self.opt._validate(value)
380             except ValueError, err:
381                 raise ValueError(_("invalid value {0} "
382                                    "for option {1}: {2}").format(str(value),
383                                                                  self.opt._name, err))
384
385     def pop(self, key, force=False):
386         """the list value can be updated (poped)
387         only if the option is a master
388
389         :param key: index of the element to pop
390         :return: the requested element
391         """
392         if not force:
393             if self.opt.impl_get_multitype() == multitypes.slave:
394                 raise SlaveError(_("cannot pop a value on a multi option {0}"
395                                    " which is a slave").format(self.opt._name))
396             elif self.opt.impl_get_multitype() == multitypes.master:
397                 for slave in self.opt.impl_get_master_slaves():
398                     values = self.context.cfgimpl_get_values()
399                     if not values.is_default_owner(slave):
400                         #get multi without valid properties
401                         values.getitem(slave, validate_properties=False).pop(key, force=True)
402         #set value without valid properties
403         ret = super(Multi, self).pop(key)
404         self.context.cfgimpl_get_values()._setvalue(self.opt, self, validate_properties=not force)
405         return ret