add SubMulti
[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 tiramisu.i18n import _
23 from tiramisu.setting import log, undefined
24 from tiramisu.error import SlaveError, ConfigError
25 from .baseoption import SymLinkOption, Option
26
27
28 class MasterSlaves(object):
29     __slots__ = ('master', 'slaves')
30
31     def __init__(self, name, childs):
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):
38                 raise ValueError(_("master group {0} shall not have "
39                                    "a symlinkoption").format(name))
40             if not isinstance(child, Option):
41                 raise ValueError(_("master group {0} shall not have "
42                                    "a subgroup").format(name))
43             if not child.impl_is_multi():
44                 raise ValueError(_("not allowed option {0} "
45                                    "in group {1}"
46                                    ": this option is not a multi"
47                                    "").format(child._name, name))
48             if child._name == name:
49                 self.master = child
50             else:
51                 slaves.append(child)
52         if self.master is None:
53             raise ValueError(_('master group with wrong'
54                                ' master name for {0}'
55                                ).format(name))
56         callback, callback_params = self.master.impl_get_callback()
57         if callback is not None and callback_params is not None:
58             for key, callbacks in callback_params.items():
59                 for callbk in callbacks:
60                     if isinstance(callbk, tuple):
61                         if callbk[0] in slaves:
62                             raise ValueError(_("callback of master's option shall "
63                                                "not refered a slave's ones"))
64         #everything is ok, store references
65         self.slaves = tuple(slaves)
66         for child in childs:
67             child._master_slaves = self
68
69     def is_master(self, opt):
70         return opt == self.master
71
72     def in_same_group(self, opt):
73         return opt == self.master or opt in self.slaves
74
75     def reset(self, values):
76         for slave in self.slaves:
77             values.reset(slave)
78
79     def pop(self, values, index):
80         #FIXME pas test de meta ...
81         for slave in self.slaves:
82             if not values.is_default_owner(slave, validate_properties=False,
83                                            validate_meta=False):
84                 values._get_cached_item(slave, validate=False,
85                                         validate_properties=False
86                                         ).pop(index, force=True)
87         pass
88
89     def getitem(self, values, opt, path, validate, force_permissive,
90                 force_properties, validate_properties, slave_path=undefined,
91                 slave_value=undefined):
92         if opt == self.master:
93             return self._getmaster(values, opt, path, validate,
94                                    force_permissive, force_properties,
95                                    validate_properties, slave_path,
96                                    slave_value)
97         else:
98             return self._getslave(values, opt, path, validate,
99                                   force_permissive, force_properties,
100                                   validate_properties)
101
102     def _getmaster(self, values, opt, path, validate, force_permissive,
103                    force_properties, validate_properties, c_slave_path,
104                    c_slave_value):
105         value = values._get_validated_value(opt, path, validate,
106                                             force_permissive,
107                                             force_properties,
108                                             validate_properties)
109         if validate is True:
110             masterlen = len(value)
111             for slave in self.slaves:
112                 try:
113                     slave_path = values._get_opt_path(slave)
114                     if c_slave_path == slave_path:
115                         slave_value = c_slave_value
116                     else:
117                         slave_value = values._get_validated_value(slave,
118                                                                   slave_path,
119                                                                   False,
120                                                                   False,
121                                                                   None, False,
122                                                                   None)  # not undefined
123                     slavelen = len(slave_value)
124                     self.validate_slave_length(masterlen, slavelen, slave._name)
125                 except ConfigError:
126                     pass
127         return value
128
129     def _getslave(self, values, opt, path, validate, force_permissive,
130                   force_properties, validate_properties):
131         value = values._get_validated_value(opt, path, validate,
132                                             force_permissive,
133                                             force_properties,
134                                             validate_properties,
135                                             None)  # not undefined
136         return self.get_slave_value(values, opt, value, validate, validate_properties)
137
138     def setitem(self, values, opt, value, path):
139         if opt == self.master:
140             masterlen = len(value)
141             for slave in self.slaves:
142                 slave_path = values._get_opt_path(slave)
143                 slave_value = values._get_validated_value(slave,
144                                                           slave_path,
145                                                           False,
146                                                           False,
147                                                           None, False,
148                                                           None)  # not undefined
149                 slavelen = len(slave_value)
150                 self.validate_slave_length(masterlen, slavelen, slave._name)
151         else:
152             self.validate_slave_length(self.get_length(values), len(value),
153                                        opt._name, setitem=True)
154
155     def get_length(self, values, validate=True, slave_path=undefined,
156                    slave_value=undefined):
157         masterp = values._get_opt_path(self.master)
158         return len(self.getitem(values, self.master, masterp, validate, False,
159                                 None, True, slave_path, slave_value))
160
161     def validate_slave_length(self, masterlen, valuelen, name, setitem=False):
162         if valuelen > masterlen or (valuelen < masterlen and setitem):
163             log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, '
164                       'setitem: {2}'.format(masterlen, valuelen, setitem))
165             raise SlaveError(_("invalid len for the slave: {0}"
166                                " which has {1} as master").format(
167                                    name, self.master._name))
168
169     def get_slave_value(self, values, opt, value, validate=True,
170                         validate_properties=True):
171         """
172         if master has length 0:
173             return []
174         if master has length bigger than 0:
175             if default owner:
176                 if has callback:
177                     if return a list:
178                         list same length as master: return list
179                         list is smaller than master: return list + None
180                         list is greater than master: raise SlaveError
181                 if has default value:
182                     list same length as master: return list
183                     list is smaller than master: return list + None
184                     list is greater than master: raise SlaveError
185                 if has default_multi value:
186                     return default_multi * master's length
187             if has value:
188                 list same length as master: return list
189                 list is smaller than master: return list + None
190                 list is greater than master: raise SlaveError
191         """
192         #if slave, had values until master's one
193         path = values._get_opt_path(opt)
194         masterlen = self.get_length(values, validate, path, value)
195         valuelen = len(value)
196         if validate:
197             self.validate_slave_length(masterlen, valuelen, opt._name)
198         if valuelen < masterlen:
199             for num in range(0, masterlen - valuelen):
200                 index = valuelen + num
201                 value.append(values._get_validated_value(opt, path, True,
202                                                          False, None,
203                                                          validate_properties,
204                                                          index=index),
205                              setitem=False,
206                              force=True)
207         return value