DUNE-DAQ
DUNE Trigger and Data Acquisition software
Toggle main menu visibility
Loading...
Searching...
No Matches
dunedaq
sourcecode
conffwk
python
conffwk
schema.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/schema.py to python/conffwk/schema.py).
7
8
# Created by Andre Anjos <andre.dos.anjos@cern.ch>
9
# Wed 24 Oct 2007 05:22:49 PM CEST
10
11
"""A set of utilities to simplify OKS instrospection.
12
"""
13
import
sys
14
import
re
15
import
logging
16
from
.
import
ConfigObject
17
18
# all supported OKS types are described here
19
oks_types = {}
20
oks_types[
'bool'
] = [
'bool'
]
21
oks_types[
'integer'
] = [
's8'
,
'u8'
,
's16'
,
'u16'
,
's32'
]
22
oks_types[
'long'
] = [
'u32'
,
's64'
,
'u64'
]
23
oks_types[
'float'
] = [
'float'
,
'double'
]
24
oks_types[
'int-number'
] = oks_types[
'integer'
] + oks_types[
'long'
]
25
oks_types[
'number'
] = \
26
oks_types[
'long'
] + oks_types[
'integer'
] + oks_types[
'float'
]
27
oks_types[
'time'
] = [
'date'
,
'time'
]
28
oks_types[
'string'
] = oks_types[
'time'
] + [
'string'
,
'uid'
,
'enum'
,
'class'
]
29
30
range_regexp = re.compile(
31
r'(?P<s1>-?0?x?[\da-fA-F]+(\.\d+)?)-(?P<s2>-?0?x?[\da-fA-F]+(\.\d+)?)'
)
32
33
34
def
decode_range
(s):
35
"""Decodes a range string representation, returns a tuple with 2 values.
36
37
This is the supported format in regexp representation:
38
'([-0x]*\\d+)\\D+-?\\d+'
39
"""
40
if
s.find(
'..'
) != -1:
41
# print 'range: %s => %s' % (s, s.split('..'))
42
return
s.split(
'..'
)
43
k = range_regexp.match(s)
44
if
k:
45
# print 'range: %s => %s' % (s, (k.group('s1'), k.group('s2')))
46
return
(k.group(
's1'
), k.group(
's2'
))
47
# print 'value: %s' % s
48
return
s
49
50
51
def
str2integer
(v, t, max):
52
"""Converts a value v to integer, irrespectively of its formatting.
53
54
If the number starts with a '0', we convert it using an octal
55
representation. Else, we try a decimal conversion. If any of these fail,
56
we try an hexa conversion before throwing a ValueError.
57
58
Keyword arguments:
59
60
v -- the value to be converted
61
t -- the python type (int or float) to use in the conversion
62
"""
63
if
isinstance(v, t):
64
return
v
65
if
not
v:
66
return
v
67
if
isinstance(v, tuple)
or
isinstance(v, list):
68
return
[
str2integer
(k, t)
for
k
in
v]
69
if
isinstance(v, str):
70
if
v[0] ==
'*'
:
71
return
max
72
elif
v[0] ==
'0'
:
73
try
:
74
return
t(v, 8)
75
except
ValueError:
76
return
t(v, 16)
77
else
:
78
try
:
79
return
t(v)
80
except
ValueError:
81
return
t(v, 16)
82
else
:
83
return
t(v)
84
85
86
def
to_int
(v):
return
str2integer(v, int, sys.maxsize)
87
88
89
def
to_long
(v):
return
str2integer(v, int, 0xffffffffffffffff)
90
91
92
def
check_range
(v, range, range_re, pytype):
93
"""Checks the range of the value 'v' to make sure it is inside."""
94
if
isinstance(v, list):
95
for
k
in
v:
96
check_range
(k, range, range_re, pytype)
97
return
98
99
in_range =
False
100
if
type(v) == str
and
range_re
is
not
None
:
101
if
range_re.match(v):
102
in_range =
True
103
else
:
104
for
k
in
range:
105
if
isinstance(k, tuple):
106
if
v >= k[0]
and
v <= k[1]:
107
in_range =
True
108
else
:
109
if
v == k:
110
in_range =
True
111
112
if
not
in_range:
113
raise
ValueError(
'Value %s is not in range %s'
% (v, range))
114
115
116
def
check_relation
(v, rel):
117
"""Checks the value v against the relationship parameters in 'rel'."""
118
from
conffwk.dal
import
DalBase
119
120
if
not
isinstance(v, DalBase):
121
raise
ValueError(
'Relationships should be DAL objects, but %s is not'
%
122
repr(v))
123
124
# check type
125
if
rel[
'type'
]
not
in
v.oksTypes():
126
raise
ValueError(
'Object %s is not of type or subtype %s'
%
127
(repr(v), rel[
'type'
]))
128
129
130
def
check_cardinality
(v, prop):
131
"""Checks the cardinality of a certain attribute or relationship."""
132
133
# check cardinality
134
if
prop[
'multivalue'
]
and
not
isinstance(v, list):
135
raise
ValueError(
'Multivalued properties must be python lists'
)
136
elif
not
prop[
'multivalue'
]
and
isinstance(v, list):
137
raise
ValueError(
138
'Single valued properties cannot be set with python lists'
)
139
140
141
def
coerce
(v, attr):
142
"""Coerces the input value 'v' in the way the attribute expects."""
143
144
# coerce if necessary
145
if
type(v) != attr[
'python_class'
]:
146
# coerce in this case
147
if
attr[
'python_class'
]
in
[int, int]:
148
149
if
isinstance(v, str):
150
if
len(v) == 0:
151
raise
ValueError(
152
'Integer number cannot be assigned an empty string'
)
153
if
v[0] ==
'0'
:
154
try
:
155
return
attr[
'python_class'
](v, 8)
156
except
ValueError:
157
return
attr[
'python_class'
](v, 16)
158
else
:
159
try
:
160
return
attr[
'python_class'
](v)
161
except
ValueError:
162
return
attr[
'python_class'
](v, 16)
163
else
:
164
v = attr[
'python_class'
](v)
165
166
elif
attr[
'python_class'
]
is
bool:
167
if
isinstance(v, str):
168
if
v
in
[
'0'
,
'false'
]:
169
v =
False
170
else
:
171
v =
True
172
else
:
173
v = attr[
'python_class'
](v)
174
175
else
:
176
v = attr[
'python_class'
](v)
177
178
# check the range of each item, if a range was set
179
if
attr[
'range'
]:
180
check_range
(v, attr[
'range'
], attr[
'range_re'
], attr[
'python_class'
])
181
182
# for special types, do a special check
183
if
attr[
'type'
] ==
'class'
:
184
# class references must exist in the DAL the time I set it
185
from
.dal
import
__dal__
186
check_range
(v, list(__dal__.keys()),
187
attr[
'range_re'
], attr[
'python_class'
])
188
189
elif
attr[
'type'
] ==
'date'
:
190
import
time
191
try
:
192
time.strptime(v,
'%Y-%m-%d'
)
193
except
ValueError
as
e:
194
try
:
195
time.strptime(v,
'%d/%m/%y'
)
196
except
ValueError
as
e:
197
try
:
198
time.strptime(v,
'%Y-%b-%d'
)
199
except
ValueError
as
e:
200
raise
ValueError(
201
'Date types should have the format '
202
'dd/mm/yy or yyyy-mm-dd or yyyy-mon-dd: %s'
% v)
203
204
elif
attr[
'type'
] ==
'time'
:
205
import
time
206
try
:
207
time.strptime(v,
'%Y-%m-%d %H:%M:%S'
)
208
except
ValueError
as
e:
209
try
:
210
time.strptime(v,
'%d/%m/%y %H:%M:%S'
)
211
except
ValueError
as
e:
212
try
:
213
time.strptime(v,
'%Y-%b-%d %H:%M:%S'
)
214
except
ValueError
as
e:
215
raise
ValueError(
216
'Time types should have the format dd/mm/yy HH:MM:SS '
217
'or yyyy-mm-dd HH:MM:SS or yyyy-mon-dd HH:MM:SS:'
218
+ str(e))
219
220
return
v
221
222
223
def
map_coercion
(class_name, schema):
224
"""Given a schema of a class, maps coercion functions from libpyconffwk."""
225
226
cls =
ConfigObject.ConfigObject
227
228
schema[
'mapping'
] = {}
229
230
for
k, v
in
list(schema[
'attribute'
].items()):
231
typename = v[
'type'
]
232
getname = v[
'type'
]
233
if
getname
in
oks_types[
'string'
]:
234
getname =
'string'
235
if
v[
'multivalue'
]:
236
typename +=
'_vec'
237
getname +=
'_vec'
238
v[
'co_get_method'
] = getattr(cls,
'get_'
+ getname)
239
v[
'co_set_method'
] = getattr(cls,
'set_'
+ typename)
240
241
else
:
242
v[
'co_get_method'
] = getattr(cls,
'get_'
+ getname)
243
v[
'co_set_method'
] = getattr(cls,
'set_'
+ typename)
244
245
if
v[
'type'
]
in
oks_types[
'string'
]:
246
v[
'python_class'
] = str
247
elif
v[
'type'
]
in
oks_types[
'bool'
]:
248
v[
'python_class'
] = bool
249
elif
v[
'type'
]
in
oks_types[
'integer'
]:
250
v[
'python_class'
] = to_int
251
elif
v[
'type'
]
in
oks_types[
'long'
]:
252
v[
'python_class'
] = to_long
253
elif
v[
'type'
]
in
oks_types[
'float'
]:
254
v[
'python_class'
] = float
255
256
# split and coerce ranges
257
v[
'range_re'
] =
None
258
if
v[
'range'
]:
259
if
v[
'type'
] ==
'string'
:
260
v[
'range_re'
] = re.compile(v[
'range'
])
261
else
:
262
v[
'range'
] = [
decode_range
(j)
for
j
in
v[
'range'
].split(
','
)]
263
for
j
in
range(len(v[
'range'
])):
264
if
isinstance(v[
'range'
][j], str):
265
v[
'range'
][j] = v[
'python_class'
](v[
'range'
][j])
266
elif
len(v[
'range'
][j]) == 1:
267
v[
'range'
][j] = v[
'python_class'
](v[
'range'
][j][0])
268
else
:
# len(v['range'][j]) == 2 (the only other case)
269
v[
'range'
][j] = (v[
'python_class'
](v[
'range'
][j][0]),
270
v[
'python_class'
](v[
'range'
][j][1]))
271
272
# integer numbers have implicit ranges and it is better to check
273
elif
v[
'type'
]
in
oks_types[
'int-number'
]:
274
if
v[
'type'
] ==
's8'
:
275
v[
'range'
] = [(-2**7, (2**7)-1)]
276
if
v[
'type'
] ==
'u8'
:
277
v[
'range'
] = [(0, (2**8)-1)]
278
if
v[
'type'
] ==
's16'
:
279
v[
'range'
] = [(-2**15, (2**15)-1)]
280
if
v[
'type'
] ==
'u16'
:
281
v[
'range'
] = [(0, (2**16)-1)]
282
if
v[
'type'
] ==
's32'
:
283
v[
'range'
] = [(-2**31, (2**31)-1)]
284
if
v[
'type'
] ==
'u32'
:
285
v[
'range'
] = [(0, (2**32)-1)]
286
if
v[
'type'
] ==
's64'
:
287
v[
'range'
] = [(-2**63, (2**63)-1)]
288
if
v[
'type'
] ==
'u64'
:
289
v[
'range'
] = [(0, (2**64)-1)]
290
291
# coerce initial values
292
if
v[
'init-value'
]:
293
try
:
294
if
v[
'type'
]
in
oks_types[
'string'
]:
295
pass
296
elif
v[
'multivalue'
]:
297
v[
'init-value'
] = [
coerce
(j, v)
298
for
j
in
v[
'init-value'
].split(
','
)]
299
else
:
300
v[
'init-value'
] =
coerce
(v[
'init-value'
], v)
301
except
ValueError
as
e:
302
logging.warning(
'Initial value of "%s.%s" could not be '
303
'coerced: %s'
%
304
(class_name, k, e))
305
306
# if the type is a date or time type, and there is not default,
307
# set "now"
308
if
not
v[
'init-value'
]
and
v[
'type'
] ==
'date'
:
309
import
datetime
310
v[
'init-value'
] == datetime.date.today().isoformat()
311
312
# if the type is a date or time type, and there is not default,
313
# set "now"
314
if
not
v[
'init-value'
]
and
v[
'type'
] ==
'time'
:
315
import
datetime
316
now = datetime.datetime.today()
317
now.replace(microsecond=0)
318
v[
'init-value'
] == now.isoformat(sep=
' '
)
319
320
for
v
in
list(schema[
'relation'
].values()):
321
if
v[
'multivalue'
]:
322
v[
'co_get_method'
] = getattr(cls,
'get_objs'
)
323
v[
'co_set_method'
] = getattr(cls,
'set_objs'
)
324
325
else
:
326
v[
'co_get_method'
] = getattr(cls,
'get_obj'
)
327
v[
'co_set_method'
] = getattr(cls,
'set_obj'
)
328
329
return
schema
330
331
332
class
Cache
(object):
333
"""Defines a cache for all known schemas at a certain time.
334
"""
335
336
def
__init__
(self, conffwk, all=True):
337
"""Initializes the cache with information from the Configuration
338
object.
339
340
This method will browse for all declared classes in the Configuration
341
object given as input and will setup the schema for all known classes.
342
After this you can still update the cache using the update() method.
343
344
Keyword parameters:
345
346
conffwk -- The conffwk.Configuration object to use as base for the
347
current cache.
348
349
all -- A boolean indicating if I should store all the attributes and
350
relations from a certain class or just the ones directly associated
351
with a class.
352
"""
353
self.
data
= {}
354
self.
all
= all
355
self.
update
(conffwk)
356
357
def
update
(self, conffwk):
358
"""Updates this cache with information from the Configuration object.
359
360
This method will add new classes not yet know to this cache. Classes
361
with existing names will not be added. No warning is generated (this
362
should be done by the OKS layer in any case.
363
"""
364
for
k
in
conffwk.classes():
365
if
k
in
list(self.
data
.keys()):
366
continue
367
self.
data
[k] = {}
368
self.
data
[k][
'attribute'
] = conffwk.attributes(k, self.
all
)
369
self.
data
[k][
'relation'
] = conffwk.relations(k, self.
all
)
370
self.
data
[k][
'superclass'
] = conffwk.superclasses(k, self.
all
)
371
self.
data
[k][
'subclass'
] = conffwk.subclasses(k, self.
all
)
372
map_coercion
(k, self.
data
[k])
373
374
def
update_dal
(self, conffwk):
375
"""Updates this cache with information for DAL.
376
377
This method will add new DAL classes not yet know to this cache.
378
Classes with existing DAL representations will not be touched.
379
"""
380
from
.dal
import
generate
381
382
# generate
383
klasses =
generate
(conffwk, [self.
data
[k][
'dal'
]
for
k
384
in
list(self.
data
.keys())
385
if
'dal'
in
self.
data
[k]])
386
# associate
387
for
k
in
klasses:
388
self.
data
[k.pyclassName()][
'dal'
] = k
389
390
def
__getitem__
(self, key):
391
"""Gets the description of a certain class."""
392
return
self.
data
[key]
393
394
def
__str__
(self):
395
"""Prints a nice display of myself"""
396
return
'%s: %d classes loaded'
% \
397
(self.__class__.__name__, len(self.
data
)) +
'\n'
+ \
398
str(list(self.
data
.keys()))
conffwk.ConfigObject.ConfigObject
Definition
ConfigObject.py:27
conffwk.schema.Cache
Definition
schema.py:332
conffwk.schema.Cache.update
update(self, conffwk)
Definition
schema.py:357
conffwk.schema.Cache.__str__
__str__(self)
Definition
schema.py:394
conffwk.schema.Cache.__init__
__init__(self, conffwk, all=True)
Definition
schema.py:336
conffwk.schema.Cache.data
dict data
Definition
schema.py:353
conffwk.schema.Cache.__getitem__
__getitem__(self, key)
Definition
schema.py:390
conffwk.schema.Cache.update_dal
update_dal(self, conffwk)
Definition
schema.py:374
conffwk.schema.Cache.all
all
Definition
schema.py:354
conffwk.dal
Definition
dal.py:1
conffwk.schema.to_int
to_int(v)
Definition
schema.py:86
conffwk.schema.check_cardinality
check_cardinality(v, prop)
Definition
schema.py:130
conffwk.schema.to_long
to_long(v)
Definition
schema.py:89
conffwk.schema.map_coercion
map_coercion(class_name, schema)
Definition
schema.py:223
conffwk.schema.coerce
coerce(v, attr)
Definition
schema.py:141
conffwk.schema.check_relation
check_relation(v, rel)
Definition
schema.py:116
conffwk.schema.decode_range
decode_range(s)
Definition
schema.py:34
conffwk.schema.check_range
check_range(v, range, range_re, pytype)
Definition
schema.py:92
conffwk.schema.str2integer
str2integer(v, t, max)
Definition
schema.py:51
generate
Definition
generate.py:1
Generated on
for DUNE-DAQ by
1.17.0