0
|
1 |
#
|
|
2 |
# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
3 |
# All rights reserved.
|
|
4 |
# This component and the accompanying materials are made available
|
|
5 |
# under the terms of "Eclipse Public License v1.0"
|
|
6 |
# which accompanies this distribution, and is available
|
|
7 |
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
8 |
#
|
|
9 |
# Initial Contributors:
|
|
10 |
# Nokia Corporation - initial contribution.
|
|
11 |
#
|
|
12 |
# Contributors:
|
|
13 |
#
|
|
14 |
# Description:
|
|
15 |
#
|
|
16 |
|
|
17 |
"""
|
|
18 |
Container classes.
|
|
19 |
Mainly internal classed that the public data model uses internally.
|
|
20 |
"""
|
|
21 |
|
|
22 |
import re
|
|
23 |
import pickle
|
|
24 |
import logging
|
3
|
25 |
import utils
|
|
26 |
from cone.public import exceptions
|
0
|
27 |
|
|
28 |
def object_container_filter(obj,**kwargs):
|
|
29 |
""" Create a list of filter functions for each argument """
|
|
30 |
filters=[]
|
|
31 |
if kwargs.has_key('name'):
|
|
32 |
filters.append(lambda x: re.match(kwargs.get('name'), x._name))
|
|
33 |
if kwargs.has_key('path'):
|
|
34 |
filters.append(lambda x: re.match(kwargs.get('path'), x._path()))
|
|
35 |
if kwargs.has_key('type'):
|
|
36 |
filters.append(lambda x: isinstance(x, kwargs.get('type')))
|
|
37 |
if kwargs.has_key('filters'):
|
|
38 |
filters += kwargs.get('filters')
|
|
39 |
ret = []
|
|
40 |
for sobj in utils.get_list(obj):
|
|
41 |
if utils.filter(obj,filters):
|
|
42 |
ret.append(sobj)
|
|
43 |
|
|
44 |
return ret
|
|
45 |
|
|
46 |
def _apply_filter(obj,filters):
|
|
47 |
""" Create a list of filter functions for each argument """
|
|
48 |
if utils.filter(obj,filters):
|
|
49 |
return [obj]
|
|
50 |
else:
|
|
51 |
return []
|
|
52 |
|
|
53 |
""" object container adding policies """
|
|
54 |
REPLACE = 0
|
|
55 |
APPEND = 1
|
|
56 |
PREPEND = 2
|
|
57 |
ERROR = 3
|
|
58 |
|
|
59 |
class DataContainer(object):
|
|
60 |
"""
|
|
61 |
Class for data containers.
|
|
62 |
Container is a data storage that can hold several keys, where each key is unique. Each key however
|
|
63 |
can hold several values, where the active value is the last one added.
|
|
64 |
|
|
65 |
Example:
|
|
66 |
data = {'key1' :[1,2,3,4],
|
|
67 |
'key2' :['foo','bar],
|
|
68 |
'key3' :['testing'],
|
|
69 |
'path/to/key' :['some','value','in','here','too']}
|
|
70 |
|
|
71 |
The active values for keys are the last ones in the array. E.g. key1 = 4.
|
|
72 |
"""
|
|
73 |
def __init__(self):
|
|
74 |
self.data = {}
|
|
75 |
|
|
76 |
def list_keys(self):
|
|
77 |
"""
|
|
78 |
List all keys of the DataStorage.
|
|
79 |
"""
|
|
80 |
return self.data.keys()
|
|
81 |
|
|
82 |
def add_value(self,key,value):
|
|
83 |
"""
|
|
84 |
Add the value as a topmost item for the given key.
|
|
85 |
@param key: name for the key to store the data.
|
|
86 |
@param value: the value to store.
|
|
87 |
"""
|
|
88 |
if self.data.has_key(key):
|
|
89 |
self.data[key].append(value)
|
|
90 |
else:
|
|
91 |
self.data[key] = [value]
|
|
92 |
return
|
|
93 |
|
|
94 |
def remove_value(self,key,value):
|
|
95 |
"""
|
|
96 |
remove individual value of the key value array
|
|
97 |
"""
|
|
98 |
self.data[key].remove(value)
|
|
99 |
return
|
|
100 |
|
|
101 |
def remove_key(self,key):
|
|
102 |
del self.data[key]
|
|
103 |
return
|
|
104 |
|
|
105 |
def get_value(self,key):
|
|
106 |
"""
|
|
107 |
self.data = {'key1' :[1,2,3,4],
|
|
108 |
'key2' :['foo','bar],
|
|
109 |
'key3' :['testing'],
|
|
110 |
'path/to/key' :['some','value','in','here','too']}
|
|
111 |
self.get_value('key1')
|
|
112 |
4
|
|
113 |
"""
|
|
114 |
return self.data[key][-1]
|
|
115 |
|
|
116 |
def get_values(self,key):
|
|
117 |
"""
|
|
118 |
return a copy of data values inside the container
|
|
119 |
"""
|
|
120 |
values = []
|
|
121 |
values.extend(self.data[key])
|
|
122 |
return values
|
|
123 |
|
|
124 |
def flatten(self):
|
|
125 |
"""
|
|
126 |
return a new dictionary of the DataContainer data with only single values for each key,
|
|
127 |
instead of the array of values.
|
|
128 |
"""
|
|
129 |
rest = {}
|
|
130 |
for key in self.data.keys():
|
|
131 |
rest[key] = self.get_value(key)
|
|
132 |
return rest
|
|
133 |
|
|
134 |
def clear(self):
|
|
135 |
"""
|
|
136 |
Remove all data from the container.
|
|
137 |
"""
|
|
138 |
return self.data.clear()
|
|
139 |
|
|
140 |
class ContainerBase(object):
|
3
|
141 |
def __init__(self, name="",**kwargs):
|
|
142 |
if len(name.split(".")) > 1 or len(name.split("/")) > 1:
|
|
143 |
raise exceptions.InvalidRef("Illegal name for ObjectContainer %s" % name)
|
|
144 |
self._name = name
|
|
145 |
self._parent = None
|
|
146 |
self._order = []
|
|
147 |
self._children = {}
|
|
148 |
self._respath = ""
|
|
149 |
for arg in kwargs.keys():
|
|
150 |
setattr(self, arg, kwargs.get(arg))
|
|
151 |
|
|
152 |
def __getstate__(self):
|
4
|
153 |
state = self.__dict__.copy()
|
3
|
154 |
return state
|
0
|
155 |
|
3
|
156 |
def __setstate__(self, state):
|
4
|
157 |
self.__dict__.update(state)
|
3
|
158 |
|
0
|
159 |
def _set_parent(self, newparent):
|
|
160 |
"""
|
|
161 |
@param newparent: The new parent object
|
|
162 |
@return: None
|
|
163 |
"""
|
|
164 |
self._parent = newparent
|
|
165 |
|
|
166 |
def _get_parent(self):
|
|
167 |
"""
|
|
168 |
@return: existing parent object
|
|
169 |
"""
|
|
170 |
return self._parent
|
|
171 |
|
|
172 |
def _del_parent(self):
|
|
173 |
"""
|
|
174 |
Set the current parent to None
|
|
175 |
"""
|
|
176 |
self._parent = None
|
|
177 |
|
3
|
178 |
def get_store_interface(self):
|
|
179 |
"""
|
|
180 |
Get a possible store interface for this ContainerBase object
|
|
181 |
"""
|
|
182 |
return None
|
|
183 |
|
|
184 |
def get_path(self):
|
|
185 |
"""
|
|
186 |
Return the path of the object
|
|
187 |
"""
|
|
188 |
return self._respath
|
|
189 |
|
0
|
190 |
parent = property(_get_parent, _set_parent,_del_parent)
|
|
191 |
|
|
192 |
|
|
193 |
class ObjectProxy(ContainerBase):
|
|
194 |
"""
|
|
195 |
An object proxy class. The ObjectProxy overrides the python builtin methdo __getattr__
|
|
196 |
to redirect any function/member access to the subobject.
|
|
197 |
"""
|
|
198 |
def __init__(self,obj=None):
|
|
199 |
"""
|
|
200 |
"""
|
|
201 |
self._obj = obj
|
|
202 |
self._parent = None
|
|
203 |
|
|
204 |
def __getattr__(self,name):
|
|
205 |
"""
|
|
206 |
direct all not found attribute calls to the sub object getattr
|
|
207 |
"""
|
|
208 |
return getattr(self._obj,name)
|
|
209 |
|
|
210 |
|
|
211 |
# def _set_parent(self, newparent):
|
|
212 |
# """
|
|
213 |
# @param newparent: The new parent object
|
|
214 |
# @return: None
|
|
215 |
# """
|
|
216 |
# self._parent = newparent
|
|
217 |
# if isinstance(self._obj, ContainerBase):
|
|
218 |
# self._obj._set_parent(newparent)
|
|
219 |
|
|
220 |
class LoadInterface(ContainerBase):
|
|
221 |
def load(self,ref):
|
3
|
222 |
file = open(ref,"rb")
|
0
|
223 |
self._parent = None
|
|
224 |
return pickle.load(file)
|
|
225 |
|
|
226 |
def unload(self,ref, obj):
|
|
227 |
"""
|
|
228 |
unload or release
|
|
229 |
"""
|
3
|
230 |
file = open(ref,"wb")
|
0
|
231 |
pickle.dump(obj,file)
|
|
232 |
file.close()
|
|
233 |
|
|
234 |
def get_path(self):
|
|
235 |
"""
|
|
236 |
Return the path of the configuration resource
|
|
237 |
"""
|
|
238 |
return ""
|
|
239 |
|
|
240 |
|
|
241 |
class ObjectContainer(ContainerBase):
|
|
242 |
"""
|
|
243 |
An object container class. The ObjectContainer is actually a Tree data structure. Any ObjectContainer
|
|
244 |
instance can include any number of children, that must be instances of ObjectContainer.
|
|
245 |
"""
|
|
246 |
def __init__(self,name="",**kwargs):
|
|
247 |
"""
|
|
248 |
"""
|
3
|
249 |
super(ObjectContainer,self).__init__(name, **kwargs)
|
0
|
250 |
|
|
251 |
def __getattr__(self,name):
|
|
252 |
"""
|
|
253 |
direct all not found attribute calls to the sub object getattr
|
|
254 |
"""
|
|
255 |
try:
|
|
256 |
return self.__dict__['_children'][name]
|
|
257 |
except KeyError:
|
3
|
258 |
try:
|
|
259 |
return getattr(super(ObjectContainer),name)
|
|
260 |
except AttributeError,e:
|
|
261 |
raise AttributeError("%s object has not attribute '%s'" % (self.__class__, name))
|
0
|
262 |
|
|
263 |
def _path(self, toparent=None):
|
|
264 |
"""
|
|
265 |
Get the path to this ObjectContainer.
|
|
266 |
@param toparent: the _parent object up to which the path is relative. Default value is None.,
|
|
267 |
which gives the fully qualified path
|
|
268 |
@return: The path to the ObjectContainer from toparent
|
|
269 |
"""
|
|
270 |
if self == toparent:
|
|
271 |
return ""
|
|
272 |
elif self._parent and self._parent != toparent:
|
|
273 |
# return the path with list index if the given element is in a list
|
|
274 |
if utils.is_list(self.parent._get(self._name)):
|
|
275 |
return self._parent._path(toparent)+"."+"%s[%s]" % (self._name,self.get_index())
|
|
276 |
else:
|
|
277 |
return self._parent._path(toparent)+"."+self._name
|
|
278 |
else:
|
|
279 |
return self._name
|
|
280 |
|
3
|
281 |
def _add(self, child_or_children, policy=REPLACE):
|
0
|
282 |
"""
|
3
|
283 |
Add a child object or multiple child objects.
|
|
284 |
@param child_or_children: The child object or list of child objects to add.
|
|
285 |
The children need to be instances of ObjectContainer.
|
0
|
286 |
@param policy: The policy which is used when an object with same name exists already
|
|
287 |
"""
|
3
|
288 |
if isinstance(child_or_children, list):
|
|
289 |
objs = child_or_children
|
|
290 |
if policy == PREPEND:
|
|
291 |
objs = reversed(objs)
|
|
292 |
policy_first = PREPEND
|
|
293 |
policy_rest = PREPEND
|
|
294 |
else:
|
|
295 |
policy_first = policy
|
|
296 |
policy_rest = APPEND
|
|
297 |
|
|
298 |
for i, obj in enumerate(objs):
|
|
299 |
if i == 0: p = policy_first
|
|
300 |
else: p = policy_rest
|
|
301 |
self._add(obj, p)
|
|
302 |
return
|
|
303 |
|
|
304 |
|
0
|
305 |
# check that the child is a supported type
|
3
|
306 |
child = child_or_children
|
0
|
307 |
if not self._supported_type(child):
|
|
308 |
raise exceptions.IncorrectClassError("Cannot add instance of %s to %s." % (child.__class__,self.__class__))
|
|
309 |
if policy == REPLACE:
|
|
310 |
self._replace(child)
|
|
311 |
elif policy == ERROR:
|
|
312 |
self._error(child)
|
|
313 |
elif policy == APPEND:
|
|
314 |
self._append(child)
|
|
315 |
elif policy == PREPEND:
|
|
316 |
self._prepend(child)
|
|
317 |
|
|
318 |
def _append(self, child):
|
|
319 |
"""
|
|
320 |
Add the given child to the proper key. Create a list entry if necessary
|
|
321 |
"""
|
|
322 |
child._set_parent(self)
|
|
323 |
if not self._children.has_key(child._name):
|
|
324 |
# skip all internal objects (that start with _)
|
|
325 |
if not child._name.startswith('?'):
|
|
326 |
self._order.append(child._name)
|
|
327 |
self._children[child._name] = child
|
|
328 |
else:
|
|
329 |
""" Create a list under the child name """
|
|
330 |
self._children[child._name] = utils.add_list(self._children[child._name], child)
|
|
331 |
return
|
|
332 |
|
|
333 |
def _prepend(self, child):
|
|
334 |
"""
|
|
335 |
Add the given child to the proper key. Create a list entry if necessary
|
|
336 |
"""
|
|
337 |
child._set_parent(self)
|
|
338 |
if not self._children.has_key(child._name):
|
|
339 |
# skip all internal objects (that start with _)
|
|
340 |
if not child._name.startswith('?'):
|
|
341 |
self._order.insert(0,child._name)
|
|
342 |
self._children[child._name] = child
|
|
343 |
else:
|
|
344 |
""" Create a list under the child name """
|
|
345 |
self._children[child._name] = utils.prepend_list(self._children[child._name], child)
|
|
346 |
return
|
|
347 |
|
|
348 |
def _replace(self, child):
|
|
349 |
"""
|
|
350 |
If the given child already exists => Replace the child,
|
|
351 |
but maintain the current children of that child
|
|
352 |
"""
|
|
353 |
child._set_parent(self)
|
|
354 |
# skip all internal objects (that start with _)
|
|
355 |
if not self._children.has_key(child._name):
|
|
356 |
if not child._name.startswith('?'):
|
|
357 |
self._order.append(child._name)
|
|
358 |
else:
|
3
|
359 |
"""
|
|
360 |
if the existing child is a instance of ObjectContainer,
|
|
361 |
add all children of the existing container to this new object, except if the
|
|
362 |
child already exists in the new child.
|
|
363 |
"""
|
0
|
364 |
existingchild = self._children[child._name]
|
|
365 |
if isinstance(existingchild, ObjectContainer):
|
|
366 |
for subchild in existingchild._objects():
|
3
|
367 |
if not child._children.has_key(subchild._name):
|
|
368 |
child._add(subchild)
|
|
369 |
|
0
|
370 |
|
|
371 |
self._children[child._name] = child
|
|
372 |
return
|
|
373 |
|
|
374 |
def _error(self, child):
|
|
375 |
"""
|
|
376 |
If the given child already exists => raise an exception.
|
|
377 |
@raise exceptions.AlreadyExists:
|
|
378 |
"""
|
|
379 |
child._set_parent(self)
|
|
380 |
if not self._children.has_key(child._name):
|
|
381 |
# skip all internal objects (that start with _)
|
|
382 |
if not child._name.startswith('?'):
|
|
383 |
self._order.insert(0,child._name)
|
|
384 |
self._children[child._name] = child
|
|
385 |
else:
|
|
386 |
raise exceptions.AlreadyExists('Child %s already exists' % child._name)
|
|
387 |
return
|
|
388 |
|
|
389 |
def _add_to_path(self, path, child, policy=REPLACE):
|
|
390 |
"""
|
|
391 |
Add a child object.
|
|
392 |
@param path: the path for the object
|
|
393 |
@param child: The child object to add
|
|
394 |
@param namespace: The namespace of the object, which defines where the object is created
|
|
395 |
"""
|
|
396 |
# check that the child is a supported type
|
|
397 |
if not self._supported_type(child):
|
|
398 |
raise exceptions.IncorrectClassError("Cannot add instance of %s to %s Container" % (child.__class__,self.__class__))
|
|
399 |
# ensure that the elements to the namespace exist
|
|
400 |
curelem = self
|
|
401 |
for ppath in utils.dottedref.split_ref(path):
|
|
402 |
|
|
403 |
if not curelem._children.has_key(ppath):
|
|
404 |
# Create missing elem
|
|
405 |
curelem._add(self._default_object(ppath))
|
|
406 |
curelem = curelem._get(ppath)
|
|
407 |
curelem._add(child,policy)
|
|
408 |
|
|
409 |
def _get(self, path):
|
|
410 |
"""
|
|
411 |
Get a child object by it path.
|
|
412 |
@return: The child object if it is found.
|
|
413 |
@raise NotFound: when object is not found from the children.
|
|
414 |
"""
|
|
415 |
|
|
416 |
try:
|
|
417 |
# traverse to the actual child element
|
|
418 |
curelem = self
|
|
419 |
for pathelem in utils.dottedref.split_ref(path):
|
|
420 |
if utils.dottedref.get_index(pathelem) == None:
|
|
421 |
curelem = curelem._children[pathelem]
|
|
422 |
else:
|
|
423 |
# If the given pathelem is referring to a list
|
|
424 |
name = utils.dottedref.get_name(pathelem)
|
|
425 |
index = utils.dottedref.get_index(pathelem)
|
|
426 |
curelem = utils.get_list(curelem._children[name])[index]
|
|
427 |
return curelem
|
|
428 |
# Catch the KeyError exception from dict and IndexError from list
|
3
|
429 |
except (KeyError,IndexError), e:
|
|
430 |
raise exceptions.NotFound("Child %s not found from %s! %s" % (path, self, e))
|
0
|
431 |
|
|
432 |
def _has(self, path):
|
|
433 |
"""
|
|
434 |
Returns True if an element under the path is found.
|
|
435 |
@return: Boolean value.
|
|
436 |
"""
|
|
437 |
|
|
438 |
try:
|
|
439 |
# traverse to the actual child element
|
|
440 |
curelem = self
|
|
441 |
for pathelem in utils.dottedref.split_ref(path):
|
|
442 |
curelem = curelem._children[pathelem]
|
|
443 |
return True
|
|
444 |
except KeyError:
|
|
445 |
return False
|
|
446 |
|
|
447 |
def _remove(self, path):
|
|
448 |
"""
|
|
449 |
Remove a child object by it path.
|
|
450 |
"""
|
|
451 |
# if the patherence is a long patherence (dotted name)
|
|
452 |
# first get the _parent object and call the remove to the _parent
|
|
453 |
(parentref,name) = utils.dottedref.psplit_ref(path)
|
|
454 |
if parentref != "":
|
|
455 |
self._get(parentref)._remove(name)
|
|
456 |
elif utils.dottedref.get_index(path) != None and \
|
|
457 |
self._get(utils.dottedref.get_name(path)):
|
|
458 |
# Delete If the given pathelem is referring to a list
|
|
459 |
name = utils.dottedref.get_name(path)
|
|
460 |
index = utils.dottedref.get_index(path)
|
|
461 |
del self._children[name][index]
|
|
462 |
if len(self._children[name]) == 0:
|
|
463 |
del self._order[self._order.index(name)]
|
|
464 |
elif self._get(path) != None: # delete if the child is found
|
|
465 |
del self._children[path]
|
3
|
466 |
# hidded children are not added to the order list
|
|
467 |
if not path.startswith('?'):
|
|
468 |
del self._order[self._order.index(path)]
|
0
|
469 |
|
|
470 |
else:
|
|
471 |
raise exceptions.NotFound("Child %s not found!" % path)
|
|
472 |
|
|
473 |
def _list_traverse(self,**kwargs):
|
|
474 |
"""
|
|
475 |
Return a list of all children paths. This function calls internally __traverse__, see it for
|
|
476 |
more details.
|
|
477 |
@return: an unordered list of children paths. The path is relative to this node.
|
|
478 |
"""
|
|
479 |
return [child._path(self) for child in self._traverse(**kwargs)]
|
|
480 |
|
|
481 |
def _traverse(self, **kwargs):
|
|
482 |
"""
|
|
483 |
The traverse goes recursively through the tree of children of this node and returns a result set as list.
|
|
484 |
Arguments can be passed to it to filter out elements of the result set. All arguments are
|
|
485 |
given as dict, so they must be given with name. E.g. _traverse(name='test')
|
|
486 |
@param name: The node name or part of name which is used as a filter. This is a regular expression (uses internally re.match())
|
|
487 |
@param path: The path name or part of name which is used as a filter. This is a regular expression (uses internally re.match())
|
3
|
488 |
@param type: The type (class) of the objects that should be returned (this can be a tuple of types)
|
|
489 |
@param depth: The max recursion depth that traverse goes through.
|
0
|
490 |
@param filters: A list of predefined filters can be given as lambda functions. E.g. filters=[lambda x: isinstance(x._obj, FooClass)]
|
|
491 |
@return: a list of ObjectContainer objects.
|
|
492 |
"""
|
|
493 |
filterlist=[]
|
|
494 |
if kwargs.has_key('ref'):
|
|
495 |
filterlist.append(lambda x: re.match(kwargs.get('ref'), x.ref))
|
|
496 |
if kwargs.has_key('name'):
|
|
497 |
filterlist.append(lambda x: re.match(kwargs.get('name'), x._name))
|
|
498 |
if kwargs.has_key('path'):
|
|
499 |
filterlist.append(lambda x: re.match(kwargs.get('path'), x._path()))
|
|
500 |
if kwargs.has_key('type'):
|
|
501 |
filterlist.append(lambda x: isinstance(x, kwargs.get('type')))
|
|
502 |
if kwargs.has_key('filters'):
|
|
503 |
filterlist += kwargs.get('filters')
|
|
504 |
|
|
505 |
ret = []
|
|
506 |
for child in self._objects():
|
3
|
507 |
subchildren = child._tail_recurse(_apply_filter,filters=filterlist,depth=kwargs.get('depth',-1))
|
0
|
508 |
ret += subchildren
|
|
509 |
return ret
|
|
510 |
|
|
511 |
def _find_leaves(self, **kwargs):
|
|
512 |
"""
|
|
513 |
Find all leaf nodes in the tree that satisfy the given filtering criteria.
|
|
514 |
|
|
515 |
For possible keyword arguments see _traverse().
|
|
516 |
|
|
517 |
@return: A list of ObjectContainer objects.
|
|
518 |
"""
|
|
519 |
# Find all children
|
|
520 |
nodes = self._traverse(**kwargs)
|
|
521 |
|
|
522 |
# Filter out non-leaves
|
|
523 |
return filter(lambda node: len(node._objects()) == 0, nodes)
|
|
524 |
|
|
525 |
def _tail_recurse(self, function, **kwargs):
|
|
526 |
"""
|
|
527 |
Run a tail recursion on all container children and execute the given function.
|
|
528 |
1. function will receive self as argument to it.
|
|
529 |
2. function will receive all kwargs as argument to it.
|
|
530 |
3. tail recursion means that the function is executed first and then the
|
|
531 |
recursion continues.
|
|
532 |
@param function: the function which is executed
|
|
533 |
@param kwargs: a list of arguments as dict
|
|
534 |
@return: an list of objects, which can be anything that the funtion returns
|
|
535 |
"""
|
3
|
536 |
depth = kwargs.get('depth',-1)
|
0
|
537 |
ret = []
|
3
|
538 |
# check the if the recursion maximum depth has been reached
|
|
539 |
# if not reached but set, decrease it by one and set that to subrecursion
|
|
540 |
if depth != 0:
|
|
541 |
ret += function(self,kwargs.get('filters',[]))
|
|
542 |
kwargs['depth'] = depth - 1
|
|
543 |
for child in self._objects():
|
|
544 |
try:
|
|
545 |
# We wont add the object to the ret until we know that it is a valid object
|
|
546 |
subchildren = child._tail_recurse(function,**kwargs)
|
|
547 |
#ret += function(child,**kwargs)
|
|
548 |
ret += subchildren
|
|
549 |
except exceptions.InvalidObject,e:
|
|
550 |
# remove the invalid object from this container
|
|
551 |
logging.getLogger('cone').warning('Removing invalid child because of exception %s' % e)
|
|
552 |
self._remove(child._name)
|
|
553 |
continue
|
|
554 |
|
0
|
555 |
return ret
|
|
556 |
|
|
557 |
def _head_recurse(self, function,**kwargs):
|
|
558 |
"""
|
|
559 |
Run a tail recursion on all container children and execute the given function.
|
|
560 |
1. function will receive self as argument to it.
|
|
561 |
2. function will receive all kwargs as argument to it.
|
|
562 |
3. head recursion means that the recursion continues to the leaf nodes and then the
|
|
563 |
execution of the function begins.
|
|
564 |
@param function: the function which is executed
|
|
565 |
@param kwargs: a list of arguments as dict
|
|
566 |
@return: an list of objects, which can be anything that the funtion returns
|
|
567 |
"""
|
|
568 |
ret = []
|
|
569 |
for child in self._objects():
|
|
570 |
try:
|
|
571 |
ret += child._head_recurse(function,**kwargs)
|
|
572 |
ret += function(child,**kwargs)
|
|
573 |
except exceptions.InvalidObject,e:
|
|
574 |
# remove the invalid object from this container
|
|
575 |
logging.getLogger('cone').warning('Removing invalid child because of exception %s' % e)
|
|
576 |
self._remove(child._name)
|
|
577 |
continue
|
|
578 |
return ret
|
|
579 |
|
|
580 |
def _list(self):
|
|
581 |
"""
|
|
582 |
Return a array of immediate children names.
|
|
583 |
@return: an unordered list of immediate children path-references
|
|
584 |
"""
|
|
585 |
# skip all internal objects (that start with _)
|
|
586 |
return [name for name in self._order if not name.startswith('?')]
|
|
587 |
|
|
588 |
def _objects(self, **kwargs):
|
|
589 |
"""
|
|
590 |
Return a array of immediate children.
|
|
591 |
@return: an unordered list of immediate children
|
|
592 |
"""
|
|
593 |
ret = []
|
|
594 |
for cname in self._order:
|
|
595 |
try:
|
|
596 |
if object_container_filter(self._children[cname], **kwargs):
|
|
597 |
ret += utils.get_list(self._children[cname])
|
|
598 |
except exceptions.InvalidObject,e:
|
|
599 |
# remove the invalid object from this container
|
|
600 |
logging.getLogger('cone').warning('Removing invalid child because of exception %s' % e)
|
|
601 |
self._remove(cname)
|
|
602 |
continue
|
|
603 |
return ret
|
|
604 |
|
|
605 |
def _get_index(self, name):
|
|
606 |
"""
|
|
607 |
Get the index of a child object by its name. The index matches the index
|
|
608 |
of the child object in the _children array.
|
|
609 |
@return: integer.
|
|
610 |
@raise NotFound: when object is not found from the children.
|
|
611 |
"""
|
|
612 |
|
|
613 |
try:
|
|
614 |
return self._order.index(name)
|
|
615 |
except KeyError:
|
|
616 |
raise exceptions.NotFound("Child %s not found!" % name)
|
|
617 |
|
|
618 |
def _supported_type(self,obj):
|
|
619 |
"""
|
|
620 |
An internal function to check that the given object is a supported for this Tree.
|
|
621 |
This is used in every __add__ operation to check whether the object can be added to the tree.
|
|
622 |
This function should be overloaded by a subclass if the supported types need to be changed.
|
|
623 |
@return: True if object is supported, otherwise false.
|
|
624 |
"""
|
3
|
625 |
return isinstance(obj, (ObjectContainer,ContainerBase))
|
0
|
626 |
|
|
627 |
def _default_object(self,name):
|
|
628 |
"""
|
|
629 |
An internal function to create a default object for this container in case of __add_to_path__, which
|
|
630 |
creates the intermediate objects automatically.
|
|
631 |
This function should be overloaded by a subclass if the default object need to be changed.
|
|
632 |
@return: A new object.
|
|
633 |
"""
|
|
634 |
return ObjectContainer(name)
|
|
635 |
|
|
636 |
def _find_parent(self, **kwargs):
|
|
637 |
"""
|
|
638 |
find a _parent object by arguments. You can define any number of object attributes that
|
|
639 |
have to match to the object.
|
|
640 |
Example1:
|
|
641 |
_find_parent(foobar=True) searches for a _parent
|
|
642 |
object which has a member attribute foobar and its value is True.
|
|
643 |
Example2:
|
|
644 |
_find_parent(name="test") searches for a _parent
|
|
645 |
object which has a member attribute name and its value is "test".
|
|
646 |
Example3: type is a special case
|
|
647 |
_find_parent(type=Configuration) searches for a _parent
|
|
648 |
object which is an instance of Configuration (checked with isinstance).
|
|
649 |
@param kwargs:
|
|
650 |
@return: The object that matches the arguments
|
|
651 |
@raise exceptions.NotFound: When no matching parent is found
|
|
652 |
"""
|
|
653 |
type = kwargs.get('type', None)
|
|
654 |
if hasattr(self,'_parent') and self._parent != None:
|
|
655 |
found = True
|
|
656 |
for key in kwargs.keys():
|
|
657 |
try:
|
|
658 |
# handle type as a special case
|
|
659 |
if key == 'type':
|
|
660 |
if not isinstance(self._parent, kwargs.get(key)):
|
|
661 |
found = False
|
|
662 |
break
|
|
663 |
elif key == 'match':
|
|
664 |
if not self._parent == kwargs.get(key):
|
|
665 |
found = False
|
|
666 |
break
|
|
667 |
elif not getattr(self._parent, key) == kwargs.get(key):
|
|
668 |
found = False
|
|
669 |
break
|
|
670 |
except AttributeError:
|
|
671 |
found = False
|
|
672 |
break
|
|
673 |
if found:
|
|
674 |
return self._parent
|
|
675 |
else:
|
|
676 |
return self._parent._find_parent(**kwargs)
|
|
677 |
else:
|
|
678 |
raise exceptions.NotFound("Parent not found!")
|
|
679 |
|
|
680 |
def _find_parent_or_default(self, default=None,**kwargs):
|
|
681 |
"""
|
|
682 |
Calls internally the find parent function, which is encapsulated with try except
|
|
683 |
returns the given default value if find parent raises NotFound exception.
|
|
684 |
"""
|
|
685 |
try:
|
|
686 |
return self._find_parent(**kwargs)
|
|
687 |
except exceptions.NotFound:
|
|
688 |
return default
|
|
689 |
|
|
690 |
def set_ref(self,ref):
|
|
691 |
"""
|
|
692 |
@param ref: The new reference of the object
|
|
693 |
"""
|
|
694 |
self._name = ref
|
|
695 |
self.ref = ref
|
|
696 |
|
|
697 |
def get_ref(self):
|
|
698 |
"""
|
|
699 |
@return: The reference of the object.
|
|
700 |
"""
|
|
701 |
return self.ref
|
|
702 |
|
3
|
703 |
def has_ref(self, ref):
|
|
704 |
"""
|
|
705 |
Check if object container contains the given reference.
|
|
706 |
@param ref: reference
|
|
707 |
"""
|
|
708 |
return self._has(ref)
|
|
709 |
|
0
|
710 |
class ObjectProxyContainer(ObjectProxy,ObjectContainer):
|
|
711 |
"""
|
|
712 |
Combines the Container and Proxy classes to one.
|
|
713 |
"""
|
|
714 |
def __init__(self,obj=None,name=""):
|
|
715 |
"""
|
|
716 |
"""
|
|
717 |
ObjectContainer.__init__(self,name)
|
|
718 |
ObjectProxy.__init__(self,obj)
|
|
719 |
|
|
720 |
def __getattr__(self,name):
|
|
721 |
"""
|
|
722 |
First check if the requested attr is a children then
|
|
723 |
direct all not found attribute calls to the sub object getattr
|
|
724 |
"""
|
|
725 |
try:
|
|
726 |
return self.__dict__['_children'][name]
|
|
727 |
except KeyError:
|
|
728 |
return getattr(self._obj,name)
|
3
|
729 |
|
|
730 |
class LoadContainer(ContainerBase):
|
|
731 |
"""
|
|
732 |
This class is meant for loading & unloading an object(s), to a ObjectContainer.
|
|
733 |
The loading is done if the object container methods are accessed.
|
|
734 |
"""
|
|
735 |
def __init__(self, path, store_interface=None):
|
|
736 |
"""
|
|
737 |
@param path: the path which is used in loading
|
|
738 |
@param store_interface: the loading interface object, which is used.
|
|
739 |
Expects load(path) and dump(obj) functions
|
|
740 |
"""
|
|
741 |
super(LoadContainer, self).__init__()
|
|
742 |
self._parent = None
|
|
743 |
self._container = None
|
|
744 |
self._storeint = store_interface
|
|
745 |
self._respath = path
|
|
746 |
|
|
747 |
def __getattr__(self,name):
|
|
748 |
"""
|
|
749 |
Load the container objects if they are not allready loaded
|
|
750 |
"""
|
|
751 |
if not self._container:
|
|
752 |
self._load()
|
|
753 |
return getattr(self._container,name)
|
|
754 |
|
|
755 |
def _load(self):
|
|
756 |
""" If the loading of the object fails => Raise an InvalidObject exception """
|
|
757 |
try:
|
|
758 |
self._container = ObjectContainer()
|
|
759 |
# this should be modified to support loading multiple elements
|
|
760 |
intf = self.get_store_interface()
|
|
761 |
# Do not try to load the objects if interface cannot be found
|
|
762 |
if intf:
|
|
763 |
obj = intf.load(self.get_full_path())
|
|
764 |
self._container._add(obj)
|
|
765 |
except exceptions.NotResource,e:
|
|
766 |
logging.getLogger('cone').warning("Loading %s from parent %s failed! %s" % (self.path,self.get_parent_path(), e))
|
|
767 |
raise exceptions.InvalidObject("Invalid configuration object %s" % self.path)
|
|
768 |
|
|
769 |
def _unload(self):
|
|
770 |
# go through objects in the container
|
|
771 |
intf = self.get_store_interface()
|
|
772 |
for obj in self._container._objects():
|
|
773 |
# remove the parent link
|
|
774 |
obj._parent = None
|
|
775 |
if intf:
|
|
776 |
intf.unload(self.get_full_path(), obj)
|
|
777 |
self._container._remove(obj._name)
|
|
778 |
# set the container back to None
|
|
779 |
self._container = None
|
|
780 |
|
|
781 |
def get_store_interface(self):
|
|
782 |
if not self._storeint and self._parent:
|
|
783 |
try:
|
|
784 |
self._storeint = self._parent.get_store_interface()
|
|
785 |
except exceptions.NotFound:
|
|
786 |
# If project is not found, let the store interface be None
|
|
787 |
pass
|
|
788 |
return self._storeint
|
|
789 |
|
|
790 |
def get_parent_path(self):
|
|
791 |
"""
|
|
792 |
Return the path of the configuration resource
|
|
793 |
"""
|
|
794 |
if self._parent:
|
|
795 |
return utils.resourceref.get_path(self._parent.get_path())
|
|
796 |
else:
|
|
797 |
return ""
|
|
798 |
|
|
799 |
def get_full_path(self, obj=None):
|
|
800 |
"""
|
|
801 |
Return the path of the configuration resource
|
|
802 |
"""
|
|
803 |
if obj != None:
|
|
804 |
try:
|
|
805 |
return obj.get_full_path()
|
|
806 |
except AttributeError:
|
|
807 |
pass
|
|
808 |
# default path processing returns the fullpath of this elem
|
|
809 |
parent_path = self.get_parent_path()
|
|
810 |
return utils.resourceref.join_refs([parent_path,self.get_path()])
|
|
811 |
|
|
812 |
|
|
813 |
class LoadLink(ContainerBase):
|
|
814 |
"""
|
|
815 |
This class is meant for loading & unloading an object(s), to a ObjectContainer.
|
|
816 |
The loading is done if the object container methods are accessed.
|
|
817 |
"""
|
|
818 |
def __init__(self, path, store_interface=None):
|
|
819 |
"""
|
|
820 |
@param path: the path which is used in loading
|
|
821 |
@param store_interface: the loading interface object, which is used.
|
|
822 |
Expects load(path) and dump(obj) functions
|
|
823 |
"""
|
|
824 |
super(LoadLink, self).__init__()
|
|
825 |
self._parent = None
|
|
826 |
self._loaded = False
|
|
827 |
self._storeint = store_interface
|
|
828 |
self._respath = path
|
|
829 |
|
|
830 |
def populate(self):
|
|
831 |
"""
|
|
832 |
Populate the object to the parent
|
|
833 |
"""
|
|
834 |
if self._parent == None:
|
|
835 |
raise exceptions.NoParent("Cannot populate a LoadLink object without existing parent object")
|
|
836 |
if not self._loaded:
|
|
837 |
for obj in self._load():
|
|
838 |
self._parent._add(obj)
|
|
839 |
|
|
840 |
def _load(self):
|
|
841 |
""" If the loading of the object fails => Raise an InvalidObject exception """
|
|
842 |
objs = []
|
|
843 |
try:
|
|
844 |
# this should be modified to support loading multiple elements
|
|
845 |
intf = self.get_store_interface()
|
|
846 |
# Do not try to load the objects if interface cannot be found
|
|
847 |
if intf:
|
|
848 |
obj = intf.load(self.get_full_path())
|
|
849 |
objs.append(obj)
|
|
850 |
except exceptions.NotResource,e:
|
|
851 |
logging.getLogger('cone').warning("Loading %s from parent %s failed! %s" % (self.path,self.get_parent_path(), e))
|
|
852 |
raise exceptions.InvalidObject("Invalid configuration object %s" % self.path)
|
|
853 |
return objs
|
|
854 |
|
|
855 |
def _unload(self):
|
|
856 |
pass
|
|
857 |
|
|
858 |
def get_store_interface(self):
|
|
859 |
if not self._storeint and self._parent:
|
|
860 |
try:
|
|
861 |
self._storeint = self._parent.get_store_interface()
|
|
862 |
except exceptions.NotFound:
|
|
863 |
# If project is not found, let the store interface be None
|
|
864 |
pass
|
|
865 |
return self._storeint
|
|
866 |
|
|
867 |
def get_parent_path(self):
|
|
868 |
"""
|
|
869 |
Return the path of the configuration resource
|
|
870 |
"""
|
|
871 |
if self._parent:
|
|
872 |
return utils.resourceref.get_path(self._parent.get_path())
|
|
873 |
else:
|
|
874 |
return ""
|
|
875 |
|
|
876 |
def get_full_path(self, obj=None):
|
|
877 |
"""
|
|
878 |
Return the path of the configuration resource
|
|
879 |
"""
|
|
880 |
if obj != None:
|
|
881 |
try:
|
|
882 |
return obj.get_full_path()
|
|
883 |
except AttributeError:
|
|
884 |
pass
|
|
885 |
# default path processing returns the fullpath of this elem
|
|
886 |
parent_path = self.get_parent_path()
|
|
887 |
return utils.resourceref.join_refs([parent_path,self.get_path()])
|
|
888 |
|
|
889 |
|
|
890 |
class LoadProxy(ContainerBase):
|
|
891 |
"""
|
|
892 |
This class is meant for representing any object loading & unloading an object,
|
|
893 |
when it is actually needed.
|
|
894 |
object
|
|
895 |
"""
|
|
896 |
def __init__(self, path, store_interface=None):
|
|
897 |
"""
|
|
898 |
@param path: the path which is used in loadin
|
|
899 |
@param store_interface: the loading interface object, which is used.
|
|
900 |
Expects load(path) and dump(obj) functions
|
|
901 |
"""
|
|
902 |
self.set('_obj', None)
|
|
903 |
self.set('_parent', None)
|
|
904 |
self.set('path', path)
|
|
905 |
self.set('_storeint', store_interface)
|
|
906 |
|
|
907 |
def __getattr__(self,name):
|
|
908 |
"""
|
|
909 |
direct all not found attribute calls to the sub object getattr
|
|
910 |
"""
|
|
911 |
if not self._obj:
|
|
912 |
self._load()
|
|
913 |
return getattr(self._obj,name)
|
|
914 |
|
|
915 |
def __getstate__(self):
|
|
916 |
"""
|
|
917 |
Return a state which should have sufficient info to load the proxy object but
|
|
918 |
dont serialize the object itself.
|
|
919 |
"""
|
|
920 |
state = {}
|
|
921 |
state['path'] = self.path
|
|
922 |
state['_obj'] = None
|
|
923 |
# state['_parent'] = self._parent
|
|
924 |
state['_storeint'] = self._storeint
|
|
925 |
return state
|
|
926 |
|
|
927 |
def __setstate__(self, state):
|
|
928 |
self.set('_obj', state.get('_obj',None))
|
|
929 |
self.set('_storeint', state.get('_storeint',None))
|
|
930 |
self.set('_parent', state.get('_parent',self._storeint))
|
|
931 |
self.set('path', state.get('path',''))
|
|
932 |
|
|
933 |
def __setattr__(self, name, value):
|
|
934 |
"""
|
|
935 |
direct attribute setting calls to the sub object setattr
|
|
936 |
"""
|
|
937 |
if not self._obj:
|
|
938 |
self._load()
|
|
939 |
setattr(self._obj,name,value)
|
|
940 |
|
|
941 |
def __delattr__(self, name):
|
|
942 |
"""
|
|
943 |
direct attribute setting calls to the sub object setattr
|
|
944 |
"""
|
|
945 |
if not self._obj:
|
|
946 |
self._load()
|
|
947 |
delattr(self._obj,name)
|
|
948 |
|
|
949 |
def _set_parent(self, newparent):
|
|
950 |
"""
|
|
951 |
@param newparent: The new parent object
|
|
952 |
@return: None
|
|
953 |
"""
|
|
954 |
self.set('_parent',newparent)
|
|
955 |
if self._obj:
|
|
956 |
self._obj._parent = self._parent
|
|
957 |
|
|
958 |
def _set_obj(self, obj):
|
|
959 |
self.set('_obj',obj)
|
|
960 |
# set the same _parent for the actual object as is stored for the proxy
|
|
961 |
if self._obj:
|
|
962 |
self._obj._parent = self._parent
|
|
963 |
self._obj.set_path(self.path)
|
|
964 |
|
|
965 |
def _get_obj(self):
|
|
966 |
if not self._obj:
|
|
967 |
self._load()
|
|
968 |
return self._obj
|
|
969 |
|
|
970 |
def _load(self):
|
|
971 |
# Should the loading of layer external resources be supported?
|
|
972 |
# E.g. resources with absolute path relative to the storage (starts with slash)
|
|
973 |
""" If the loading of the object fails => Raise an InvalidObject exception """
|
|
974 |
try:
|
|
975 |
obj = self.get_store_interface().load(self.fullpath)
|
|
976 |
self._set_obj(obj)
|
|
977 |
obj.set_ref(utils.resourceref.to_objref(self.path))
|
|
978 |
except exceptions.NotResource,e:
|
|
979 |
logging.getLogger('cone').warning("Loading %s from parent %s failed! %s" % (self.path,self.get_parent_path(), e))
|
|
980 |
raise exceptions.InvalidObject("Invalid configuration object %s" % self.path)
|
|
981 |
|
|
982 |
def _unload(self):
|
|
983 |
if self._obj:
|
|
984 |
self.get_store_interface().unload(self.fullpath, self._obj)
|
|
985 |
self.set('_obj',None)
|
|
986 |
|
|
987 |
def get_store_interface(self):
|
|
988 |
if not self._storeint:
|
|
989 |
self.set('_storeint',self._parent.get_store_interface())
|
|
990 |
return self._storeint
|
|
991 |
|
|
992 |
def set(self,name,value):
|
|
993 |
"""
|
|
994 |
Proxy has a specific attribute setting function, because by default all attributes are
|
|
995 |
stored to the actual proxy object
|
|
996 |
"""
|
|
997 |
self.__dict__[name] = value
|
|
998 |
|
|
999 |
def get(self,name):
|
|
1000 |
"""
|
|
1001 |
Proxy has also a specific attribute getting function, because by default all attributes are
|
|
1002 |
stored to the actual proxy object
|
|
1003 |
"""
|
|
1004 |
return self.__dict__[name]
|
|
1005 |
|
|
1006 |
def save(self):
|
|
1007 |
if hasattr(self._obj,'save'):
|
|
1008 |
self._obj.save()
|
|
1009 |
self._unload()
|
|
1010 |
|
|
1011 |
def close(self):
|
|
1012 |
if hasattr(self._obj,'close'):
|
|
1013 |
self._obj.close()
|
|
1014 |
|
|
1015 |
def get_parent_path(self):
|
|
1016 |
"""
|
|
1017 |
Return the path of the configuration resource
|
|
1018 |
"""
|
|
1019 |
if self._parent:
|
|
1020 |
return utils.resourceref.get_path(self._parent.get_path())
|
|
1021 |
else:
|
|
1022 |
return ""
|
|
1023 |
|
|
1024 |
def get_path(self):
|
|
1025 |
"""
|
|
1026 |
Return the path of the configuration resource
|
|
1027 |
"""
|
|
1028 |
if self._obj:
|
|
1029 |
return self._obj.get_path()
|
|
1030 |
else:
|
|
1031 |
return self.path
|
|
1032 |
|
|
1033 |
@property
|
|
1034 |
def fullpath(self):
|
|
1035 |
"""
|
|
1036 |
Return the path of the configuration resource
|
|
1037 |
"""
|
|
1038 |
try:
|
|
1039 |
return self._obj.get_full_path()
|
|
1040 |
except AttributeError:
|
|
1041 |
parent_path = self.get_parent_path()
|
|
1042 |
return utils.resourceref.join_refs([parent_path,self.path])
|