better support for sqlalchemy storage
[tiramisu.git] / tiramisu / storage / dictionary / option.py
1 # -*- coding: utf-8 -*-
2 ""
3 # Copyright (C) 2014 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 tiramisu.i18n import _
21 from tiramisu.setting import undefined
22 from tiramisu.error import ConfigError
23
24
25 #____________________________________________________________
26 #
27 # Base
28 class StorageBase(object):
29     __slots__ = ('_name', '_requires', '_properties', '_readonly',
30                  '_calc_properties', '_informations',
31                  '_state_readonly', '_state_requires', '_stated',
32                  '_multi', '_validator', '_validator_params',
33                  '_default', '_default_multi', '_state_callback', '_callback',
34                  '_state_callback_params', '_callback_params', '_multitype',
35                  '_consistencies', '_warnings_only', '_master_slaves',
36                  '_state_consistencies', '_extra', '_subdyn', '__weakref__',
37                  '_state_master_slaves', '_choice_values',
38                  '_choice_values_params')
39
40     def __init__(self):
41         try:
42             self._subdyn
43         except AttributeError:
44             self._subdyn = None
45         try:
46             self._consistencies
47         except AttributeError:
48             self._consistencies = []
49         try:
50             self._callback
51         except AttributeError:
52             self._callback = None
53         try:
54             self._callback_params
55         except AttributeError:
56             self._callback_params = {}
57         try:
58             self._validator
59         except AttributeError:
60             self._validator = None
61         try:
62             self._validator_params
63         except AttributeError:
64             self._validator_params = None
65
66     def _add_consistency(self, func, all_cons_opts, params):
67         self._consistencies.append((func, all_cons_opts, params))
68
69     def _get_consistencies(self):
70         return self._consistencies
71
72     def _get_id(self):
73         return id(self)
74
75     def _impl_getsubdyn(self):
76         return self._subdyn
77
78     def _impl_setsubdyn(self, subdyn):
79         self._subdyn = subdyn
80
81     def commit(self):
82         pass
83
84
85 class StorageOptionDescription(StorageBase):
86     __slots__ = ('_children', '_cache_paths', '_cache_consistencies',
87                  '_group_type', '_is_build_cache', '_state_group_type')
88
89     def __init__(self):
90         self._cache_paths = None
91
92     def _add_children(self, child_names, children):
93         self._children = (tuple(child_names), tuple(children))
94
95     def impl_already_build_caches(self):
96         return self._is_build_cache
97
98     def impl_get_opt_by_path(self, path):
99         try:
100             return self._cache_paths[0][self._cache_paths[1].index(path)]
101         except ValueError:  # pragma: optional cover
102             raise AttributeError(_('no option for path {0}').format(path))
103
104     def impl_get_path_by_opt(self, opt):
105         try:
106             return self._cache_paths[1][self._cache_paths[0].index(opt)]
107         except ValueError:  # pragma: optional cover
108             raise AttributeError(_('no option {0} found').format(opt))
109
110     def impl_get_group_type(self):  # pragma: optional cover
111         return self._group_type
112
113     def impl_build_cache_option(self, _currpath=None, cache_path=None,
114                                 cache_option=None):
115         try:
116             self._cache_paths
117         except AttributeError:
118             self._cache_paths = None
119         if _currpath is None and self._cache_paths is not None:  # pragma: optional cover
120             # cache already set
121             return
122         if _currpath is None:
123             save = True
124             _currpath = []
125         else:
126             save = False
127         if cache_path is None:
128             cache_path = []
129             cache_option = []
130         for option in self._impl_getchildren(dyn=False):
131             attr = option._name
132             path = str('.'.join(_currpath + [attr]))
133             cache_option.append(option)
134             cache_path.append(path)
135             if option.impl_is_optiondescription():
136                 _currpath.append(attr)
137                 option.impl_build_cache_option(_currpath, cache_path,
138                                                cache_option)
139                 _currpath.pop()
140         if save:
141             self._cache_paths = (tuple(cache_option), tuple(cache_path))
142
143     def impl_get_options_paths(self, bytype, byname, _subpath, only_first, context):
144         find_results = []
145
146         def _rebuild_dynpath(path, suffix, dynopt):
147             found = False
148             spath = path.split('.')
149             for length in xrange(1, len(spath)):
150                 subpath = '.'.join(spath[0:length])
151                 subopt = self.impl_get_opt_by_path(subpath)
152                 if dynopt == subopt:
153                     found = True
154                     break
155             if not found:
156                 raise ConfigError(_('cannot find dynpath'))
157             subpath = subpath + suffix
158             for slength in xrange(length, len(spath)):
159                 subpath = subpath + '.' + spath[slength] + suffix
160             return subpath
161
162         def _filter_by_name(path, option):
163             name = option.impl_getname()
164             if option._is_subdyn():
165                 if byname.startswith(name):
166                     found = False
167                     for suffix in option._subdyn._impl_get_suffixes(
168                             context):
169                         if byname == name + suffix:
170                             found = True
171                             path = _rebuild_dynpath(path, suffix,
172                                                     option._subdyn)
173                             option = option._impl_to_dyn(
174                                 name + suffix, path)
175                             break
176                     if not found:
177                         return False
178             else:
179                 if not byname == name:
180                     return False
181             find_results.append((path, option))
182             return True
183
184         def _filter_by_type(path, option):
185             if isinstance(option, bytype):
186                 #if byname is not None, check option byname in _filter_by_name
187                 #not here
188                 if byname is None:
189                     if option._is_subdyn():
190                         name = option.impl_getname()
191                         for suffix in option._subdyn._impl_get_suffixes(
192                                 context):
193                             spath = _rebuild_dynpath(path, suffix,
194                                                      option._subdyn)
195                             find_results.append((spath, option._impl_to_dyn(
196                                 name + suffix, spath)))
197                     else:
198                         find_results.append((path, option))
199                 return True
200             return False
201
202         def _filter(path, option):
203             if bytype is not None:
204                 retval = _filter_by_type(path, option)
205                 if byname is None:
206                     return retval
207             if byname is not None:
208                 return _filter_by_name(path, option)
209
210         opts, paths = self._cache_paths
211         for index in range(0, len(paths)):
212             option = opts[index]
213             if option.impl_is_optiondescription():
214                 continue
215             path = paths[index]
216             if _subpath is not None and not path.startswith(_subpath + '.'):
217                 continue
218             if bytype == byname is None:
219                 if option._is_subdyn():
220                     name = option.impl_getname()
221                     for suffix in option._subdyn._impl_get_suffixes(
222                             context):
223                         spath = _rebuild_dynpath(path, suffix,
224                                                  option._subdyn)
225                         find_results.append((spath, option._impl_to_dyn(
226                             name + suffix, spath)))
227                 else:
228                     find_results.append((path, option))
229             else:
230                 if _filter(path, option) is False:
231                     continue
232             if only_first:
233                 return find_results[0]
234         return find_results
235
236     def _impl_st_getchildren(self, context, only_dyn=False):
237         for child in self._children[1]:
238             if only_dyn is False or child.impl_is_dynoptiondescription():
239                 yield(child)
240
241     def _getattr(self, name, suffix=undefined, context=undefined, dyn=True):
242         error = False
243         if suffix is not undefined:
244             try:
245                 if undefined in [suffix, context]:  # pragma: optional cover
246                     raise ConfigError(_("suffix and context needed if "
247                                         "it's a dyn option"))
248                 if name.endswith(suffix):
249                     oname = name[:-len(suffix)]
250                     child = self._children[1][self._children[0].index(oname)]
251                     return self._impl_get_dynchild(child, suffix)
252                 else:
253                     error = True
254             except ValueError:  # pragma: optional cover
255                 error = True
256         else:
257             try:  # pragma: optional cover
258                 if name == '_readonly':
259                     raise AttributeError(_("{0} instance has no attribute "
260                                          "'_readonly'").format(
261                                              self.__class__.__name__))
262                 child = self._children[1][self._children[0].index(name)]
263                 if dyn and child.impl_is_dynoptiondescription():
264                     error = True
265                 else:
266                     return child
267             except ValueError:
268                 child = self._impl_search_dynchild(name, context=context)
269                 if child != []:
270                     return child
271                 error = True
272         if error:
273             raise AttributeError(_('unknown Option {0} '
274                                    'in OptionDescription {1}'
275                                    '').format(name, self._name))
276
277     def _get_force_store_value(self):
278         #FIXME faire des tests (notamment pas ajouter à un config)
279         #FIXME devrait faire un cache !
280         opts, paths = self._cache_paths
281         for index in range(0, len(paths)):
282             opt = opts[index]
283             path = paths[index]
284             if 'force_store_value' in opt._properties:
285                 yield (opt, path)