1 3
from .attributes import *
2 3
from .modules import *
3

4

5 3
class ModuleProperty(ModuleAttribute):
6
    """
7
    A property for a submodule.
8

9
    The ModuleAttribute is declared with:
10
    ModuleAttribute(module_cls, default, doc)
11

12
    The module_cls is instantiated in the __init__ of the parent module
13

14
    For the moment, the following actions are supported:
15
       - module.sub = dict(...) : module.sub.set_setup_attributes(dict(...))
16
       - module.sub: returns the submodule.
17
    """
18 3
    default = {}
19

20 3
    def __init__(self,
21
                 module_cls,
22
                 default=None,
23
                 doc="",
24
                 ignore_errors=False,
25
                 call_setup=False,
26
                 **kwargs):
27 3
        self.module_cls = module_cls
28 3
        self.kwargs = kwargs
29 3
        ModuleAttribute.__init__(self,
30
                               default=default,
31
                               doc=doc,
32
                               ignore_errors=ignore_errors,
33
                               call_setup=call_setup)
34

35 3
    def set_value(self, obj, val):
36
        """
37
        Use the dictionnary val to set_setup_attributes
38
        :param obj:
39
        :param val:
40
        :return:
41
        """
42 0
        getattr(obj, self.name).setup_attributes = val
43 0
        return val
44

45 3
    def get_value(self, obj):
46 3
        if not hasattr(obj, '_' + self.name):
47
            # getter must manage the instantiation of default value
48 3
            setattr(obj, '_' + self.name,
49
                self._create_module(obj))
50 3
        return getattr(obj, '_' + self.name)
51

52 3
    def _create_module(self, obj):
53 3
        return self.module_cls(obj, name=self.name, **self.kwargs)
54

55

56 3
class ModuleList(Module, list):
57
    """ a list of modules"""
58 3
    def __init__(self, parent, name=None, element_cls=Module, default=[]):
59 0
        def element_name(element_self):
60
            """ function that is used to dynamically assign each
61
            ModuleListElement's name to the index in the list.
62
            This is needed for proper storage in the config file"""
63 0
            try:
64 0
                return element_self.parent.index(element_self)
65 0
            except ValueError:
66 0
                return element_self._initial_name #'not in list' #return len(element_self.parent)
67 0
        def element_next(element_self):
68 0
            try:
69 0
                return element_self.parent[element_self.parent.index(element_self)+1]
70 0
            except:
71 0
                return None
72 0
        def element_init(element_self, parent, initial_name=None, *args, **kwargs):
73
            # creates a wrapper around the init function to pass the initial element
74
            # number in the list at object creation
75 0
            element_self._initial_name = initial_name
76 0
            return element_cls.__init__(element_self, parent, *args, **kwargs)
77
        # element.name equals element.number in order to get the right config
78
        # file section
79 0
        self.element_cls = type(element_cls.__name__ + "ListElement",
80
                                (element_cls, ),
81
                                {'name': property(fget=element_name),
82
                                 'next': property(fget=element_next),
83
                                 '__init__': element_init
84
                                })
85 0
        self._signal_launcher = self.element_cls._signal_launcher
86 0
        super(ModuleList, self).__init__(parent, name=name)
87
        # set to default setting
88 0
        self.extend(default)
89

90
    # all read-only methods from the base class 'list' work perfectly well for us, i.e.
91
    # __getitem__, count(), index(), reverse()
92 3
    def __setitem__(self, index, value):
93
        # setting a list element sets up the corresponding module
94 0
        self[index].setup_attributes = value
95

96 3
    def insert(self, index, new):
97
        # insert None as a placeholder at the right place in the list
98
        # in order to assign right indices to surrounding elements
99 0
        super(ModuleList, self).insert(index, None)
100
        # make new module (initial_name must be given).
101 0
        super(ModuleList, self).__setitem__(index,
102
                            self.element_cls(self, initial_name=index))
103
        # set initial name to none, since name is now inferred from index in the list
104 0
        self[index]._initial_name=None
105
        # initialize setup_attributes
106 0
        self[index].setup_attributes = new
107
        # save state
108 0
        self.save_state()
109

110 3
    def append(self, new):
111 0
        self.insert(self.__len__(), new)
112

113 3
    def extend(self, iterable):
114 0
        for i in iterable:
115 0
            self.append(i)
116

117 3
    def __delitem__(self, index=-1):
118
        # make sure at object destruction that the name variable corresponds to former name
119 0
        self[index]._initial_name = index
120
        # setting a list element sets up the corresponding module
121 0
        to_delete = super(ModuleList, self).pop(index)
122
        # call destructor
123 0
        to_delete._clear()
124
        # remove saved state from config file
125 0
        self.c._pop(index)
126
        #self.save_state()
127

128 3
    def pop(self, index=-1):
129
        # get attributes
130 0
        setup_attributes = self[index].setup_attributes
131 0
        self.__delitem__(index)
132 0
        return setup_attributes
133

134 3
    def remove(self, value):
135 0
        self.__delitem__(self.index(value))
136

137 3
    def __repr__(self):
138 0
        return str(ModuleList.__name__)+"("+list.__repr__(self)+")"
139

140 3
    @property
141
    def setup_attributes(self):
142 0
        return [item.setup_attributes for item in self]
143

144 3
    @setup_attributes.setter
145
    def setup_attributes(self, val):
146 0
        for i, v in enumerate(val):
147 0
            try:
148 0
                self[i] = v
149 0
            except IndexError:
150 0
                self.append(v)
151 0
        while len(self) > len(val):
152 0
            self.__delitem__(-1)
153

154 3
    def _load_setup_attributes(self):
155
        """
156
         Load and sets all setup attributes from config file
157
        """
158 0
        if self.c is not None:
159
            # self.c._data is a list that can be passed to setup_attributes
160 0
            self.setup_attributes = self.c._data
161

162

163 3
class ModuleListProperty(ModuleProperty):
164
    """
165
    A property for a list of submodules.
166
    """
167 3
    default = []
168 3
    module_cls = ModuleList
169

170 3
    def __init__(self, element_cls, default=None, doc="", ignore_errors=False):
171
        # only difference to base class: need to assign element_cls (i.e. class of element modules)
172 3
        self.element_cls = element_cls
173 3
        ModuleProperty.__init__(self,
174
                                self.module_cls,
175
                                default=default,
176
                                doc=doc,
177
                                ignore_errors=ignore_errors)
178

179 3
    def _create_module(self, obj):
180 0
        newmodule = self.module_cls(obj,
181
                                    name=self.name,
182
                                    element_cls=self.element_cls,
183
                                    default=self.default)
184 0
        try:
185 0
            newmodule._widget_class = self._widget_class
186 0
        except AttributeError:
187 0
            pass
188 0
        return newmodule
189

190 3
    def validate_and_normalize(self, obj, value):
191
        """ ensures that only list-like values are passed to the ModuleProperty """
192 0
        if not isinstance(value, list):
193 0
            try:
194 0
                value = value.values()
195 0
            except AttributeError:
196 0
                raise ValueError("ModuleProperty must be assigned a list. "
197
                                 "You have wrongly assigned an object of type "
198
                                 "%s. ", type(value))
199 0
        return value
200

201

202 3
class ModuleDict(Module):
203
    """
204
    container class that loosely resembles a dictionary which contains submodules
205
    """
206 3
    def __getitem__(self, key):
207 0
        return getattr(self, key)
208

209 3
    def keys(self):
210 0
        return self._module_attributes
211

212 3
    def values(self):
213 0
        return [self[k] for k in self.keys()]
214

215 3
    def items(self):
216 0
        return [(k, self[k]) for k in self.keys()]
217

218 3
    def __iter__(self):
219
        # this method allows to write code like this: 'for submodule in modulecontainer: submodule.do_sth()'
220 0
        return iter(self.values())
221

222 3
    @property
223
    def setup_attributes(self):
224 0
        return super(ModuleDict, self).setup_attributes
225

226 3
    @setup_attributes.setter
227
    def setup_attributes(self, kwds):
228 0
        Module.setup_attributes.fset(self,
229
            {k: v for k, v in kwds.items() if k in self._setup_attributes})
230

231 3
    def __setitem__(self, key, value):
232
        # make the new ModuleProperty of module type "value" in the class
233 0
        mp = ModuleProperty(value)
234 0
        mp.name = key  # assign module name
235 0
        setattr(self.__class__, key, mp)
236
        # do what the constructor would do: append to setup_attributes...
237 0
        self._module_attributes.append(key)
238 0
        self._setup_attributes.append(key)
239
        # ... attribute the name
240 0
        self[key].name = key
241
        # initialize with saved values if available
242 0
        self[key]._load_setup_attributes()
243

244 3
    def __delitem__(self, key):
245 0
        self._module_attributes.pop(key)
246 0
        self._setup_attributes.pop(key)
247 0
        getattr(self, key)._clear()
248 0
        delattr(self, key)
249

250 3
    def pop(self, key):
251
        """ same as __delattr__ (does not return a value) """
252 0
        module = self._setup_attributes.pop(key)
253 0
        delattr(self, key)
254 0
        return module
255

256

257 3
class ModuleDictProperty(ModuleProperty):
258 3
    default_module_cls = Module
259

260 3
    def __init__(self, module_cls=None, default=None, doc="",
261
                 ignore_errors=False, **kwargs):
262
        """
263
        returns a descriptor for a module container, i.e. a class that contains submodules whose name and class are
264
        specified in kwargs. module_cls is the base class for the module container (typically SoftwareModule)
265
        """
266
        # get default base class to inherit from
267 3
        if module_cls is None:
268 3
            module_cls = self.default_module_cls
269
        # make a copy of ModuleDict class that can be modified without modifying all ModuleDict class instances
270
        # inherit from module_cls
271 3
        ModuleDictClassInstance = type(module_cls.__name__+"DictPropertyInstance",
272
                                       (ModuleDict, module_cls),
273
                    {key: ModuleProperty(value) for key, value in kwargs.items()})
274
        # metaclass of Module already takes care of _setup/module_attributes
275
        # and names of submodules, so no need for these two:
276
        # ModuleDictClassInstance._setup_attributes = kwargs.keys()
277
        # ModuleDictClassInstance._module_attributes = kwargs.keys()
278

279
        # init this attribute with the contained module
280 3
        super(ModuleDictProperty, self).__init__(ModuleDictClassInstance,
281
                                                 default=default,
282
                                                 doc=doc,
283
                                                 ignore_errors=ignore_errors)
284

Read our documentation on viewing source code .

Loading