update sqlalchemy storage for values et settings
[tiramisu.git] / tiramisu / option / masterslave.py
1 # -*- coding: utf-8 -*-
2 "master slave support"
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 it
6 # under the terms of the GNU Lesser General Public License as published by the
7 # Free Software Foundation, either version 3 of the License, or (at your
8 # option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
13 # details.
14 #
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 # The original `Config` design model is unproudly borrowed from
19 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
20 # the whole pypy projet is under MIT licence
21 # ____________________________________________________________
22 from ..i18n import _
23 from ..setting import log, undefined
24 from ..error import SlaveError, PropertiesOptionError
25 from .baseoption import DynSymLinkOption, SymLinkOption, Option
26
27
28 class MasterSlaves(object):
29     __slots__ = ('master', 'slaves')
30
31     def __init__(self, name, childs, validate=True):
32         #if master (same name has group) is set
33         #for collect all slaves
34         self.master = None
35         slaves = []
36         for child in childs:
37             if isinstance(child, SymLinkOption):  # pragma: optional cover
38                 raise ValueError(_("master group {0} shall not have "
39                                    "a symlinkoption").format(name))
40             if not isinstance(child, Option):  # pragma: optional cover
41                 raise ValueError(_("master group {0} shall not have "
42                                    "a subgroup").format(name))
43             if not child.impl_is_multi():  # pragma: optional cover
44                 raise ValueError(_("not allowed option {0} "
45                                    "in group {1}"
46                                    ": this option is not a multi"
47                                    "").format(child.impl_getname(), name))
48             if child.impl_getname() == name:
49                 self.master = child
50             else:
51                 if child.impl_getdefault() != []:
52                     raise ValueError(_("not allowed default value for option {0} "
53                                        "in group {1}").format(child.impl_getname(),
54                                                               name))
55                 slaves.append(child)
56         if self.master is None:  # pragma: optional cover
57             raise ValueError(_('master group with wrong'
58                                ' master name for {0}'
59                                ).format(name))
60         if validate:
61             callback, callback_params = self.master.impl_get_callback()
62             if callback is not None and callback_params != {}:  # pragma: optional cover
63                 for key, callbacks in callback_params.items():
64                     for callbk in callbacks:
65                         if isinstance(callbk, tuple):
66                             if callbk[0] in slaves:
67                                 raise ValueError(_("callback of master's option shall "
68                                                    "not refered a slave's ones"))
69         #everything is ok, store references
70         self.slaves = tuple(slaves)
71         for child in childs:
72             child._master_slaves = self
73
74     def is_master(self, opt):
75         return opt == self.master or (isinstance(opt, DynSymLinkOption) and
76                                       opt._opt == self.master)
77
78     def getmaster(self, opt):
79         if isinstance(opt, DynSymLinkOption):
80             suffix = opt.impl_getsuffix()
81             name = self.master.impl_getname() + suffix
82             base_path = opt._dyn.split('.')[0] + '.'
83             path = base_path + name
84             master = self.master._impl_to_dyn(name, path)
85         else:  # pragma: no dynoptiondescription cover
86             master = self.master
87         return master
88
89     def getslaves(self, opt):
90         if isinstance(opt, DynSymLinkOption):
91             for slave in self.slaves:
92                 suffix = opt.impl_getsuffix()
93                 name = slave.impl_getname() + suffix
94                 base_path = opt._dyn.split('.')[0] + '.'
95                 path = base_path + name
96                 yield slave._impl_to_dyn(name, path)
97         else:  # pragma: no dynoptiondescription cover
98             for slave in self.slaves:
99                 yield slave
100
101     def in_same_group(self, opt):
102         if isinstance(opt, DynSymLinkOption):
103             return opt._opt == self.master or opt._opt in self.slaves
104         else:  # pragma: no dynoptiondescription cover
105             return opt == self.master or opt in self.slaves
106
107     def reset(self, opt, values, setting_properties):
108         for slave in self.getslaves(opt):
109             values.reset(slave, validate=False, _setting_properties=setting_properties)
110
111     def pop(self, opt, values, index):
112         for slave in self.getslaves(opt):
113             if not values.is_default_owner(slave, validate_properties=False,
114                                            validate_meta=False, index=index):
115                 values._get_cached_value(slave, validate=False,
116                                          validate_properties=False
117                                          ).pop(index, force=True)
118         pass
119
120     def getitem(self, values, opt, path, validate, force_permissive,
121                 trusted_cached_properties, validate_properties, session,
122                 slave_path=undefined, slave_value=undefined,
123                 setting_properties=undefined, self_properties=undefined, index=None,
124                 returns_raise=False):
125         if self.is_master(opt):
126             return self._getmaster(values, opt, path, validate,
127                                    force_permissive,
128                                    validate_properties, slave_path,
129                                    slave_value, self_properties, index,
130                                    returns_raise, setting_properties, session)
131         else:
132             return self._getslave(values, opt, path, validate,
133                                   force_permissive, trusted_cached_properties,
134                                   validate_properties, setting_properties,
135                                   self_properties, index, returns_raise,
136                                   session)
137
138     def _getmaster(self, values, opt, path, validate, force_permissive,
139                    validate_properties, c_slave_path,
140                    c_slave_value, self_properties, index, returns_raise,
141                    setting_properties, session):
142         value = values._get_cached_value(opt, path=path, validate=validate,
143                                          force_permissive=force_permissive,
144                                          validate_properties=validate_properties,
145                                          self_properties=self_properties,
146                                          from_masterslave=True, index=index,
147                                          returns_raise=True,
148                                          setting_properties=setting_properties)
149         if isinstance(value, Exception):
150             return value
151         if index is None and validate is True:
152             masterlen = len(value)
153             for slave in self.getslaves(opt):
154                 slave_path = slave.impl_getpath(values._getcontext())
155                 slavelen = values._p_.get_max_length(slave_path, session)
156                 self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
157         return value
158
159     def _getslave(self, values, opt, path, validate, force_permissive,
160                   trusted_cached_properties, validate_properties, setting_properties,
161                   self_properties, index, returns_raise, session):
162         """
163         if master has length 0:
164             return []
165         if master has length bigger than 0:
166             if default owner:
167                 if has callback:
168                     if return a list:
169                         list same length as master: return list
170                         list is smaller than master: return list + None
171                         list is greater than master: raise SlaveError
172                 if has default value:
173                     list same length as master: return list
174                     list is smaller than master: return list + None
175                     list is greater than master: raise SlaveError
176                 if has default_multi value:
177                     return default_multi * master's length
178             if has value:
179                 list same length as master: return list
180                 list is smaller than master: return list + None
181                 list is greater than master: raise SlaveError
182         """
183         master = self.getmaster(opt)
184         context = values._getcontext()
185         masterp = master.impl_getpath(context)
186         masterlen = self.get_length(values, opt, session, validate, undefined,
187                                     undefined, force_permissive,
188                                     master=master, returns_raise=returns_raise)
189         if isinstance(masterlen, Exception):
190             return masterlen
191         master_is_meta = values._is_meta(master, masterp, session)
192         multi = values._get_multi(opt, path)
193         #if masterlen is [], test properties (has no value, don't get any value)
194         if masterlen == 0:
195             if validate_properties:
196                 props = context.cfgimpl_get_settings().validate_properties(opt, False,
197                                                                            False,
198                                                                            value=multi,
199                                                                            path=path,
200                                                                            force_permissive=force_permissive,
201                                                                            setting_properties=setting_properties)
202                 if props:
203                     if returns_raise:
204                         return props
205                     else:
206                         raise props
207         else:
208             one_has_value = False
209             if index is None:
210                 indexes = range(0, masterlen)
211             else:
212                 indexes = [index]
213             for idx in indexes:
214                 value = values._get_cached_value(opt, path, validate,
215                                                  force_permissive,
216                                                  trusted_cached_properties,
217                                                  validate_properties,
218                                                  with_meta=master_is_meta,
219                                                  index=idx,
220                                                  # not self_properties,
221                                                  # depends to index
222                                                  #self_properties=self_properties,
223                                                  masterlen=masterlen,
224                                                  from_masterslave=True,
225                                                  returns_raise=True)
226                 if isinstance(value, Exception):
227                     if isinstance(value, PropertiesOptionError):
228                         err = value
229                         multi.append_properties_error(value)
230                     else:
231                         return value
232                 else:
233                     multi.append(value, setitem=False, force=True, validate=validate,
234                                  force_permissive=force_permissive)
235                     one_has_value = True
236             if not one_has_value:
237                 #raise last err
238                 if returns_raise:
239                     return err
240                 else:
241                     raise err
242         return multi
243
244     def validate(self, values, opt, value, path, returns_raise, session):
245         if self.is_master(opt):
246             masterlen = len(value)
247             #for regen slave path
248             base_path = '.'.join(path.split('.')[:-1]) + '.'
249             for slave in self.getslaves(opt):
250                 slave_path = base_path + slave.impl_getname()
251                 slavelen = values._p_.get_max_length(slave_path, session)
252                 self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
253         else:
254             val_len = self.get_length(values, opt, session, slave_path=path, returns_raise=returns_raise)
255             if isinstance(val_len, Exception):
256                 return val_len
257             self.validate_slave_length(val_len,
258                                        len(value),
259                                        opt.impl_getname(), opt, setitem=True)
260
261     def get_length(self, values, opt, session, validate=True, slave_path=undefined,
262                    slave_value=undefined, force_permissive=False, master=None,
263                    masterp=None, returns_raise=False):
264         """get master len with slave option"""
265         if master is None:
266             master = self.getmaster(opt)
267         if masterp is None:
268             masterp = master.impl_getpath(values._getcontext())
269         if slave_value is undefined:
270             slave_path = undefined
271         value = self.getitem(values, master, masterp, validate,
272                              force_permissive, None, True, session, slave_path=slave_path,
273                              slave_value=slave_value, returns_raise=returns_raise)
274         if isinstance(value, Exception):
275             return value
276         return len(value)
277
278     def validate_slave_length(self, masterlen, valuelen, name, opt, setitem=False):
279         if valuelen > masterlen or (valuelen < masterlen and setitem):  # pragma: optional cover
280             log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, '
281                       'setitem: {2}'.format(masterlen, valuelen, setitem))
282             raise SlaveError(_("invalid len for the slave: {0}"
283                                " which has {1} as master").format(
284                                    name, self.getmaster(opt).impl_getname()))