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