DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
ConfigObject.py
Go to the documentation of this file.
1#!/usr/bin/env python
2# vim: set fileencoding=utf-8 :
3# Created by Andre Anjos <andre.dos.anjos@cern.ch>
4# Mon 22 Oct 2007 04:12:01 PM CEST
5
6"""A pythonic wrapper over the OKS ConfigObject wrapper.
7
8Necessary to give the user a more pythonic experience than dealing with
9std::vector objects and memory management.
10"""
11#import libpyconffwk
12import logging
13from . import dalproperty
14from ._daq_conffwk_py import _ConfigObject
15from .proxy import _DelegateMetaFunction
16
17class _ConfigObjectProxy(object,
18 metaclass=_DelegateMetaFunction):
19 memberclass = _ConfigObject
20
21
23 """ConfigObjects are generic representations of objects in OKS."""
24
25 def __init__(self, raw_object, schema, configuration):
26 """Initializes a ConfigObject in a certain Configuration database.
27
28 This method will initialize the ConfigObject and recursively any other
29 objects under it. If the number of recursions is too big, it may stop
30 python from going on. In this case, you may reset the limit with:
31
32 import sys
33 sys.setrecursionlimit(10000) # for example
34
35 raw_object -- This is the libpyconffwk.ConfigObject to initialize this
36 object from.
37
38 schema -- A pointer to the overall schema from the Configuration
39 database to which this object is associated.
40
41 configuration -- The database this object belongs too. This is needed
42 to bind the database lifetime to this object
43
44 Raises RuntimeError in case of problems.
45 """
46 super(ConfigObject, self).__init__(raw_object)
47
48 self.__schema__ = schema[self.class_name()]
49 self.__overall_schema__ = schema
50 self.__cache__ = {}
51 self.__configuration__ = configuration
52
53 def __getitem__(self, name):
54 """Returns the attribute or relation defined by 'name'.
55
56 If an attribute does not exist, instead of a wrapper exception,
57 you get an AttributeError.
58
59 Raises KeyError, if the 'name' is not a valid class item.
60 """
61 if name in self.__schema__['attribute']:
62 return (self.__schema__['attribute'][name]
63 ['co_get_method'](self, name))
64
65 elif name in self.__cache__:
66 return self.__cache__[name]
67
68 elif name in self.__schema__['relation']:
69 try:
70 data = self.__schema__[
71 'relation'][name]['co_get_method'](self, name)
72 if self.__schema__['relation'][name]['multivalue']:
73 self.__cache__[name] = \
76 for k in data]
77 else:
78 self.__cache__[name] = None
79 if data:
80 self.__cache__[name] = \
83
84 except RuntimeError as e:
85 if self.__schema__['relation'][name]['multivalue']:
86 self.__cache__[name] = []
87 else:
88 self.__cache__[name] = None
89 logging.warning('Problems retrieving relation "%s" of '
90 'object "%s". Resetting and ignoring. '
91 'OKS error: %s' %
92 (name, self.full_name(), str(e)))
93
94 return self.__cache__[name]
95
96 # shout if you get here
97 raise KeyError('"%s" is not an attribute or relation of class "%s"' %
98 (name, self.class_name()))
99
100 def __eq__(self, other):
101 """True is the 2 objects have the same class and ID and conffwk database
102 """
103 return (self.class_name() == other.class_name()) and \
104 (self.UID() == other.UID())
105
106 def __ne__(self, other):
107 """True if the 2 objects *not* have the same class and ID."""
108 return not (self == other)
109
110 def __hash__(self):
111 """True is the 2 objects have the same class and ID and conffwk database
112 """
113 return hash(self.full_name())
114
115 def __setitem__(self, name, value):
116 """Sets the attribute or relation defined by 'name'.
117
118 This method works as a wrapper around the several set functions
119 attached to ConfigObjects, by making them feel a bit more pythonic.
120 If attributes do not exist in a certain ConfigObject, we raise an
121 AttributeError. If a value cannot be set, we raise a ValueError instead
122 of the classical SWIG RuntimeErrors everywhere.
123
124 Raises AttributeError, if the 'name' is not a valid class item.
125 Raises ValueError, if I cannot set the value you want to the variable
126
127 Returns 'value', so you can daisy-chain attributes in the normal way.
128 """
129 try:
130 if name in self.__schema__['attribute']:
131 self.__schema__['attribute'][name]['co_set_method'](
132 self, name, value)
133
134 elif name in self.__schema__['relation']:
135 self.__schema__['relation'][name]['co_set_method'](
136 self, name, value)
137 self.__cache__[name] = value
138
139 else:
140 # shout if you get here
141 raise KeyError('"%s" is not an attribute or relation of '
142 'class "%s"' %
143 (name, self.class_name()))
144
145 except RuntimeError as e:
146 raise ValueError("Error setting value of variable '%s' in %s "
147 "to '%s': %s"
148 % (name, repr(self), str(value), str(e)))
149
150 return value
151
152 def __repr__(self):
153 return '<ConfigObject \'' + self.full_name() + '\'>'
154
155 def __str__(self):
156 return self.full_name() + \
157 ' (%d attributes, %d relations), inherits from %s' % \
158 (len(self.__schema__['attribute']),
159 len(self.__schema__['relation']),
160 self.__schema__['superclass'])
161
162 def update_dal(self, d, followup_method, get_method, cache=None,
163 recurse=True):
164 """Sets each attribute defined in the DAL object 'd', with the value.
165
166 This method will update the ConfigObject attributes and its
167 relationships recursively, cooperatively with the Configuration class.
168 The recursion is implemented in a very easy way in these terms.
169
170 Keyword arguments:
171
172 d -- This is the DAL object you are trying to set this
173 ConfigObject from.
174
175 followup_method -- The Configuration method to call for the recursion.
176 This one varies with the type of change you are performing (adding or
177 updating).
178
179 get_method -- The Configuration method to call for retrieving objects
180 from the associated database.
181
182 cache -- This is a cache that may be set by the Configuration object if
183 necessary. Users should *never* set this variable. This variable is
184 there to handle recursions gracefully.
185
186 recurse -- This is a boolean flag that indicates if you want to enable
187 recursion or not in the update. If set to 'True' (the default), I'll
188 recurse until all objects in the tree are updated. Otherwise, I'll not
189 recurse at all and just make sure my attributes and relationships are
190 set to what you determine they should be. Please note that if you
191 decide to update relationships, that the objects to which you are
192 pointing to should be available in the database (directly or indirectly
193 through includes) if you choose to do this non-recursively.
194
195 """
196 for k in self.__schema__['attribute'].keys():
197 if hasattr(d, k) and getattr(d, k) is not None:
198 self[k] = getattr(d, k)
199
200 for k, v in self.__schema__['relation'].items():
201 if not hasattr(d, k):
202 continue
203
204 # if you get here, d has attribute k and it is not None
205 if v['multivalue']:
206 if not getattr(d, k):
207 self[k] = []
208 else:
209 # please, note you cannot just "append" to ConfigObject
210 # multiple relationships, since a new value (i.e. a new
211 # list) is returned each time you use __getitem__.
212 # So, you have to set all in one go.
213 val = []
214 for i in getattr(d, k):
215 if cache and i.fullName() in cache:
216 val.append(cache[i.fullName()])
217 elif recurse:
218 val.append(followup_method(
219 i, cache=cache, recurse=recurse))
220 else:
221 val.append(get_method(i.className(), i.id))
222 self[k] = val
223
224 else: # this is the simplest case
225 i = getattr(d, k)
226 if i:
227 if cache and i.fullName() in cache:
228 self[k] = cache[i.fullName()]
229 elif recurse:
230 self[k] = followup_method(
231 i, cache=cache, recurse=recurse)
232 else:
233 self[k] = get_method(i.className(), i.id)
234 else:
235 self[k] = None
236
237 def as_dal(self, cache):
238 """Returns a DAL representation of myself and my descendents.
239
240 In this implementation, we by-pass the type checking facility to gain
241 in time and because we know that if the ConfigObject was set, it must
242 conform to OKS in any case.
243 """
244 dobj = self.__schema__['dal'](id=self.UID())
245 for k in dobj.oksTypes():
246 cache[k][self.UID()] = dobj
247
248 for a in self.__schema__['attribute'].keys():
249 setattr(dobj.__class__, a,
250 property(dalproperty._return_attribute(a, dobj, self[a]),
251 dalproperty._assign_attribute(a)))
252
253 for r in self.__schema__['relation'].keys():
254 data = self[r]
255
256 if self.__schema__['relation'][r]['multivalue']:
257
258 getter = dalproperty. \
259 _return_relation(r, multi=True, cache=cache,
260 data=data, dalobj=dobj)
261
262 else:
263
264 getter = dalproperty. \
265 _return_relation(r, cache=cache,
266 data=data, dalobj=dobj)
267
268 setattr(dobj.__class__, r,
269 property(getter,
270 dalproperty._assign_relation(r)))
271
272 return dobj
273
274 def set_obj(self, name, value):
275 """Sets the sigle-value relation 'name' to the provided 'value'
276
277 """
278
279 # the C++ implementation of set_obj wants
280 # a libpyconffwk.ConfigObject instance. So
281 # we have to extract it from our proxy
282
283 if value is not None:
284 value = value._obj
285
286 return super(ConfigObject, self).set_obj(name, value)
287
288 def set_objs(self, name, value):
289 """Sets the multi-value relation 'name' to the provided 'value'
290
291 """
292
293 # the C++ implementation of set_objs wants
294 # a libpyconffwk.ConfigObject instances. So
295 # we have to extract them from our proxy
296
297 if value is not None:
298 tmp = [e._obj if e is not None else e for e in value]
299 else:
300 tmp = None
301
302 return super(ConfigObject, self).set_objs(name, tmp)
update_dal(self, d, followup_method, get_method, cache=None, recurse=True)
__init__(self, raw_object, schema, configuration)