The Gaudi Framework  v37r2 (f6e0b68e)
semantics.py
Go to the documentation of this file.
1 
11 import copy
12 import logging
13 import re
14 import sys
15 from collections.abc import MutableMapping, MutableSequence
16 
17 _log = logging.getLogger(__name__)
18 is_64bits = sys.maxsize > 2**32
19 
20 
21 class PropertySemantics(object):
22  """
23  Basic property semantics implementation, with no validation/transformation.
24 
25  Not to be used directly for any actual property, use only specializations.
26  """
27 
28  __handled_types__ = ()
29 
30  def __init__(self, cpp_type):
31  self._name = None
32  self.cpp_type = cpp_type
33 
34  @property
35  def name(self):
36  return self._name
37 
38  @name.setter
39  def name(self, value):
40  self._name = value
41 
42  @property
43  def cpp_type(self):
44  return self._cpp_type
45 
46  @cpp_type.setter
47  def cpp_type(self, value):
48  if not any(
49  h.match(value) if hasattr(h, "match") else h == value
50  for h in self.__handled_types__
51  ):
52  raise TypeError("C++ type {!r} not supported".format(value))
53  self._cpp_type = value
54 
55  def load(self, value):
56  """
57  Transformation for data when reading the property.
58  """
59  return value
60 
61  def store(self, value):
62  """
63  Validation/transformation of the data to be stored.
64  """
65  return value
66 
67  def is_set(self, value):
68  """
69  Allow overriding the definition of "is set" if we need helper types.
70  """
71  return True
72 
73  def opt_value(self, value):
74  """
75  Option string version of value.
76  """
77  if hasattr(value, "__opt_value__"):
78  return value.__opt_value__()
79  return value
80 
81  def merge(self, a, b):
82  """
83  "Merge" two values.
84 
85  Used when merging two Configurable instances, by default just ensure
86  the two values do not conflict, but it can be overridden in
87  derived semantics to, for example, append to the two lists.
88  """
89  if self.store(a) != self.store(b):
90  raise ValueError("cannot merge values %r and %r" % (a, b))
91  return a
92 
93 
95  """
96  Special semantics that makes a deep copy of the default value on first access
97  and considers a property set if its value is different from the default.
98 
99  This semantics is meant to be used whenever there is no specific semantic
100  (with proper change detection) implemented for a type.
101  """
102 
103  __handled_types__ = (re.compile(r".*"),)
104 
105  def default(self, value):
106  # remember the default value we got and return a copy
107  self._default = value
108  self._is_set = False
109  return copy.deepcopy(value)
110 
111  def store(self, value):
112  # flag that the value was explicitly set
113  self._is_set = True
114  return super(DefaultSemantics, self).store(value)
115 
116  def is_set(self, value):
117  try:
118  # we assume the property was set if it was changed
119  return self._is_set or self._default != value
120  except AttributeError:
121  # either self._is_set or self._default is not defined,
122  # so the value was not explicitly set nor modified
123  # from the default
124  return False
125 
126 
128  __handled_types__ = ("std::string",)
129 
130  def store(self, value):
131  if not isinstance(value, str):
132  raise TypeError("cannot set property {} to {!r}".format(self.name, value))
133  return value
134 
135 
137  __handled_types__ = ("bool",)
138 
139  def store(self, value):
140  return bool(value)
141 
142 
144  __handled_types__ = ("float", "double")
145 
146  def store(self, value):
147  from numbers import Number
148 
149  if not isinstance(value, Number):
150  raise TypeError(
151  "number expected, got {!r} in assignment to {}".format(value, self.name)
152  )
153  return float(value)
154 
155 
157  # dictionary generated with tools/print_limits.cpp
158  INT_RANGES = {
159  "signed char": (-128, 127),
160  "short": (-32768, 32767),
161  "int": (-2147483648, 2147483647),
162  "long": (
163  (-9223372036854775808, 9223372036854775807)
164  if is_64bits
165  else (-2147483648, 2147483647)
166  ),
167  "long long": (-9223372036854775808, 9223372036854775807),
168  "unsigned char": (0, 255),
169  "unsigned short": (0, 65535),
170  "unsigned int": (0, 4294967295),
171  "unsigned long": (0, 18446744073709551615 if is_64bits else 4294967295),
172  "unsigned long long": (0, 18446744073709551615),
173  }
174 
175  __handled_types__ = tuple(INT_RANGES)
176 
177  def store(self, value):
178  from numbers import Number
179 
180  if not isinstance(value, Number):
181  raise TypeError(
182  "number expected, got {!r} in assignment to {}".format(value, self.name)
183  )
184  v = int(value)
185  if v != value:
186  _log.warning("converted %s to %d in assignment to %s", value, v, self.name)
187  min_value, max_value = self.INT_RANGES[self.cpp_type]
188  if v < min_value or v > max_value:
189  raise ValueError(
190  "value {} outside limits for {!r} {}".format(
191  v, self.cpp_type, self.INT_RANGES[self.cpp_type]
192  )
193  )
194  return v
195 
196 
197 _IDENTIFIER_RE = r"[a-zA-Z_][a-zA-Z0-9_]*"
198 _NS_IDENT_RE = r"{ident}(::{ident})*".format(ident=_IDENTIFIER_RE)
199 _COMMA_SEPARATION_RE = r"{exp}(,{exp})*"
200 
201 
203  __handled_types__ = (
204  "Algorithm",
205  "Auditor",
206  re.compile(
207  r"AlgTool(:{})?$".format(_COMMA_SEPARATION_RE.format(exp=_NS_IDENT_RE))
208  ),
209  re.compile(
210  r"Service(:{})?$".format(_COMMA_SEPARATION_RE.format(exp=_NS_IDENT_RE))
211  ),
212  )
213 
214  def __init__(self, cpp_type):
215  super(ComponentSemantics, self).__init__(cpp_type)
216  if ":" in cpp_type:
217  self.cpp_type, self.interfaces = cpp_type.split(":", 1)
218  self.interfaces = set(self.interfaces.split(","))
219  else:
220  self.cpp_type = cpp_type
221  self.interfaces = set()
222 
223  def store(self, value):
224  from . import Configurable, Configurables
225 
226  if isinstance(value, Configurable):
227  value.name # make sure the configurable has a name
228  elif isinstance(value, str):
229  # try to map the sring to an existing Configurable
230  if value in Configurable.instances:
231  value = Configurable.instances[value]
232  else:
233  # or create one from type and name
234  if "/" in value:
235  t, n = value.split("/")
236  else:
237  t = n = value
238  value = Configurables.getByType(t).getInstance(n)
239  else:
240  raise TypeError(
241  "cannot assign {!r} to {!r}, requested string or {!r}".format(
242  value, self.name, self.cpp_type
243  )
244  )
245  if value.__component_type__ != self.cpp_type:
246  raise TypeError(
247  "wrong type for {!r}: expected {!r}, got {!r}".format(
248  self.name, self.cpp_type, value.__component_type__
249  )
250  )
251  try:
252  # if no interface is declared we cannot check
253  if value.__interfaces__:
254  if not self.interfaces.issubset(value.__interfaces__):
255  raise TypeError(
256  "wrong interfaces for {!r}: required {}".format(
257  self.name, list(self.interfaces)
258  )
259  )
260  except AttributeError:
261  pass # no interfaces declared by the configrable, cannot check
262  return value
263 
264  def default(self, value):
265  return self.store(value)
266 
267 
268 def extract_template_args(cpp_type):
269  """
270  Return an iterator over the list of template arguments in a C++ type
271  string.
272 
273  >>> t = 'map<string, vector<int, allocator<int> >, allocator<v<i>, a<i>> >'
274  >>> list(extract_template_args(t))
275  ['string', 'vector<int, allocator<int> >', 'allocator<v<i>, a<i>>']
276  >>> list(extract_template_args('int'))
277  []
278  """
279  template_level = 0
280  arg_start = -1
281  for p, c in enumerate(cpp_type):
282  if c == ",":
283  if template_level == 1:
284  yield cpp_type[arg_start:p].strip()
285  arg_start = p + 1
286  elif c == "<":
287  template_level += 1
288  if template_level == 1:
289  arg_start = p + 1
290  elif c == ">":
291  template_level -= 1
292  if template_level == 0:
293  yield cpp_type[arg_start:p].strip()
294 
295 
296 class _ListHelper(MutableSequence):
297  def __init__(self, semantics):
298  self.value_semantics = semantics
299  self.default = None
300  self._data = []
301  self.is_dirty = False
302 
303  @property
304  def data(self):
305  return self._data if self.is_dirty else self.default
306 
307  def __len__(self):
308  return len(self.data)
309 
310  def __getitem__(self, key):
311  return self.value_semantics.load(self.data.__getitem__(key))
312 
313  def __setitem__(self, key, value):
314  self.is_dirty = True
315  self.data.__setitem__(key, self.value_semantics.store(value))
316 
317  def __delitem__(self, key):
318  if not self.is_dirty:
319  raise RuntimeError("cannot remove elements from the default value")
320  self.data.__delitem__(key)
321 
322  def __eq__(self, other):
323  return self.data == other
324 
325  def insert(self, key, value):
326  self.is_dirty = True
327  self.data.insert(key, self.value_semantics.store(value))
328 
329  def append(self, value):
330  self.is_dirty = True
331  self.data.append(self.value_semantics.store(value))
332 
333  def extend(self, iterable):
334  self.is_dirty = True
335  self.data.extend(self.value_semantics.store(value) for value in iterable)
336 
337  def opt_value(self):
338  return [self.value_semantics.opt_value(item) for item in self.data]
339 
340  def __repr__(self):
341  return repr(self.data)
342 
343 
345  __handled_types__ = (re.compile(r"(std::)?(vector|list)<.*>$"),)
346 
347  def __init__(self, cpp_type, valueSem=None):
348  super(SequenceSemantics, self).__init__(cpp_type)
349  self.value_semantics = valueSem or getSemanticsFor(
350  list(extract_template_args(cpp_type))[0]
351  )
352 
353  @property
354  def name(self):
355  return self._name
356 
357  @name.setter
358  def name(self, value):
359  self._name = value
360  self.value_semantics.name = "{} element".format(self._name)
361 
362  def store(self, value):
363  if not isinstance(value, (list, _ListHelper, tuple)):
364  raise TypeError(
365  "list or tuple expected, got {!r} in assignment to {}".format(
366  value, self.name
367  )
368  )
369  new_value = _ListHelper(self.value_semantics)
370  new_value.extend(value)
371  return new_value
372 
373  def default(self, value):
374  new_value = _ListHelper(self.value_semantics)
375  new_value.default = value
376  return new_value
377 
378  def opt_value(self, value):
379  """
380  Option string version of value.
381  """
382  if not isinstance(value, _ListHelper):
383  value = self.default(value)
384  return value.opt_value()
385 
386 
388  """
389  Extend the sequence-semantics with a merge-method to behave like a
390  OrderedSet: Values are unique but the order is maintained.
391  Use 'OrderedSet<T>' as fifth parameter of the Gaudi::Property<T> constructor
392  to invoke this merging method. Also applies to std::[unordered_]set.
393  """
394 
395  __handled_types__ = (
396  re.compile(r"(std::)?(unordered_)?set<.*>$"),
397  re.compile(r"^OrderedSet<.*>$"),
398  )
399 
400  def __init__(self, cpp_type):
401  super(OrderedSetSemantics, self).__init__(cpp_type)
402 
403  def merge(self, bb, aa):
404  for b in bb:
405  if b not in aa:
406  aa.append(b)
407  return aa
408 
409 
410 class _DictHelper(MutableMapping):
411  def __init__(self, key_semantics, value_semantics):
412  self.key_semantics = key_semantics
413  self.value_semantics = value_semantics
414  self.default = None
415  self._data = {}
416  self.is_dirty = False
417 
418  @property
419  def data(self):
420  return self._data if self.is_dirty else self.default
421 
422  def __len__(self):
423  return len(self.data)
424 
425  def __getitem__(self, key):
426  return self.value_semantics.load(
427  self.data.__getitem__(self.key_semantics.store(key))
428  )
429 
430  def __setitem__(self, key, value):
431  self.is_dirty = True
432  self.data.__setitem__(
433  self.key_semantics.store(key), self.value_semantics.store(value)
434  )
435 
436  def __delitem__(self, key):
437  if not self.is_dirty:
438  raise RuntimeError("cannot remove elements from the default value")
439  self.data.__delitem__(self.key_semantics.store(key))
440 
441  def __iter__(self):
442  for key in self.data:
443  yield self.key_semantics.load(key)
444 
445  def keys(self):
446  return list(self)
447 
448  def items(self):
449  for key, value in self.data.items():
450  yield (self.key_semantics.load(key), self.value_semantics.load(value))
451 
452  def values(self):
453  for value in self.data.values():
454  yield self.value_semantics.load(value)
455 
456  def __contains__(self, key):
457  return self.key_semantics.store(key) in self.data
458 
459  def get(self, key, default=None):
460  key = self.key_semantics.store(key)
461  if key in self.data:
462  return self.value_semantics.load(self.data[key])
463  return default
464 
465  # __contains__, , get, __eq__, __ne__
466  # popitem, clear, setdefault
467 
468  def update(self, otherMap):
469  self.is_dirty = True
470  for key, value in otherMap.items():
471  self.data[self.key_semantics.store(key)] = self.value_semantics.store(value)
472 
473  def opt_value(self):
474  return {
475  self.key_semantics.opt_value(key): self.value_semantics.opt_value(value)
476  for key, value in self.data.items()
477  }
478 
479  def __repr__(self):
480  return repr(self.data)
481 
482 
484  __handled_types__ = (re.compile(r"(std::)?(unordered_)?map<.*>$"),)
485 
486  def __init__(self, cpp_type):
487  super(MappingSemantics, self).__init__(cpp_type)
488  template_args = list(extract_template_args(cpp_type))
489  self.key_semantics = getSemanticsFor(template_args[0])
490  self.value_semantics = getSemanticsFor(template_args[1])
491 
492  @property
493  def name(self):
494  return self._name
495 
496  @name.setter
497  def name(self, value):
498  self._name = value
499  self.key_semantics.name = "{} key".format(self._name)
500  self.value_semantics.name = "{} value".format(self._name)
501 
502  def store(self, value):
503  # No explicit type checking as anything else than dict fails in update call
504  new_value = _DictHelper(self.key_semantics, self.value_semantics)
505  new_value.update(value)
506  return new_value
507 
508  def default(self, value):
509  new_value = _DictHelper(self.key_semantics, self.value_semantics)
510  new_value.default = value
511  return new_value
512 
513  def opt_value(self, value):
514  """
515  Option string version of value.
516  """
517  if not isinstance(value, _DictHelper):
518  value = self.default(value)
519  return value.opt_value()
520 
521 
522 SEMANTICS = [
523  c
524  for c in globals().values()
525  if isinstance(c, type)
526  and issubclass(c, PropertySemantics)
527  and c not in (PropertySemantics, DefaultSemantics)
528 ]
529 
530 
531 def getSemanticsFor(cpp_type):
532  for semantics in SEMANTICS:
533  try:
534  return semantics(cpp_type)
535  except TypeError:
536  pass
537  return DefaultSemantics(cpp_type)
GaudiConfig2.semantics.ComponentSemantics.interfaces
interfaces
Definition: semantics.py:217
GaudiConfig2.semantics.SequenceSemantics.default
def default(self, value)
Definition: semantics.py:373
GaudiConfig2.semantics.ComponentSemantics.__init__
def __init__(self, cpp_type)
Definition: semantics.py:214
GaudiConfig2.semantics.MappingSemantics.store
def store(self, value)
Definition: semantics.py:502
GaudiConfig2.semantics.PropertySemantics.__handled_types__
__handled_types__
Definition: semantics.py:28
GaudiConfig2.semantics.PropertySemantics._cpp_type
_cpp_type
Definition: semantics.py:53
GaudiConfig2.semantics.IntSemantics.store
def store(self, value)
Definition: semantics.py:177
GaudiConfig2.semantics._DictHelper.__delitem__
def __delitem__(self, key)
Definition: semantics.py:436
GaudiConfig2.semantics._ListHelper.__repr__
def __repr__(self)
Definition: semantics.py:340
GaudiConfig2.semantics.PropertySemantics.cpp_type
cpp_type
Definition: semantics.py:32
GaudiConfig2.semantics._DictHelper.__contains__
def __contains__(self, key)
Definition: semantics.py:456
GaudiConfig2.semantics.MappingSemantics.__init__
def __init__(self, cpp_type)
Definition: semantics.py:486
GaudiConfig2.semantics.ComponentSemantics.store
def store(self, value)
Definition: semantics.py:223
GaudiConfig2.semantics.PropertySemantics.is_set
def is_set(self, value)
Definition: semantics.py:67
GaudiConfig2.semantics.PropertySemantics.load
def load(self, value)
Definition: semantics.py:55
GaudiConfig2.semantics.IntSemantics
Definition: semantics.py:156
GaudiConfig2.semantics._DictHelper.get
def get(self, key, default=None)
Definition: semantics.py:459
GaudiConfig2.semantics.DefaultSemantics.store
def store(self, value)
Definition: semantics.py:111
GaudiConfig2.semantics.MappingSemantics
Definition: semantics.py:483
GaudiConfig2.semantics._ListHelper.__getitem__
def __getitem__(self, key)
Definition: semantics.py:310
GaudiConfig2.semantics._DictHelper.value_semantics
value_semantics
Definition: semantics.py:413
GaudiConfig2.semantics.DefaultSemantics._is_set
_is_set
Definition: semantics.py:108
GaudiConfig2.semantics.OrderedSetSemantics
Definition: semantics.py:387
GaudiConfig2.semantics.StringSemantics.store
def store(self, value)
Definition: semantics.py:130
GaudiConfig2.semantics.SequenceSemantics.__init__
def __init__(self, cpp_type, valueSem=None)
Definition: semantics.py:347
GaudiConfig2.semantics.getSemanticsFor
def getSemanticsFor(cpp_type)
Definition: semantics.py:531
GaudiConfig2.semantics._DictHelper.keys
def keys(self)
Definition: semantics.py:445
GaudiConfig2.semantics._ListHelper.__len__
def __len__(self)
Definition: semantics.py:307
GaudiConfig2.semantics.extract_template_args
def extract_template_args(cpp_type)
Definition: semantics.py:268
GaudiConfig2.semantics._ListHelper.__delitem__
def __delitem__(self, key)
Definition: semantics.py:317
GaudiConfig2.semantics.PropertySemantics._name
_name
Definition: semantics.py:31
GaudiConfig2.semantics._DictHelper.__len__
def __len__(self)
Definition: semantics.py:422
GaudiConfig2.semantics.BoolSemantics.store
def store(self, value)
Definition: semantics.py:139
GaudiConfig2.semantics.BoolSemantics
Definition: semantics.py:136
GaudiConfig2.semantics.FloatSemantics.store
def store(self, value)
Definition: semantics.py:146
GaudiConfig2.semantics._DictHelper.default
default
Definition: semantics.py:414
GaudiConfig2.semantics._ListHelper.data
data
Definition: semantics.py:323
GaudiConfig2.semantics.MappingSemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:513
GaudiConfig2.semantics._DictHelper.opt_value
def opt_value(self)
Definition: semantics.py:473
GaudiConfig2.semantics.OrderedSetSemantics.__init__
def __init__(self, cpp_type)
Definition: semantics.py:400
GaudiConfig2.semantics.PropertySemantics.name
def name(self)
Definition: semantics.py:35
GaudiConfig2.semantics._ListHelper
Definition: semantics.py:296
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
GaudiConfig2.semantics.SequenceSemantics.value_semantics
value_semantics
Definition: semantics.py:349
GaudiConfig2.semantics._ListHelper._data
_data
Definition: semantics.py:300
GaudiConfig2.semantics.DefaultSemantics._default
_default
Definition: semantics.py:107
GaudiConfig2.semantics.SequenceSemantics.name
def name(self)
Definition: semantics.py:354
GaudiConfig2.semantics._DictHelper.values
def values(self)
Definition: semantics.py:452
GaudiConfig2.semantics._DictHelper.__repr__
def __repr__(self)
Definition: semantics.py:479
GaudiConfig2.semantics._DictHelper
Definition: semantics.py:410
GaudiConfig2.semantics.SequenceSemantics.store
def store(self, value)
Definition: semantics.py:362
GaudiConfig2.semantics._DictHelper.__iter__
def __iter__(self)
Definition: semantics.py:441
GaudiConfig2.semantics._ListHelper.insert
def insert(self, key, value)
Definition: semantics.py:325
GaudiConfig2.semantics._ListHelper.value_semantics
value_semantics
Definition: semantics.py:298
GaudiConfig2.semantics._ListHelper.append
def append(self, value)
Definition: semantics.py:329
GaudiConfig2.semantics._DictHelper.update
def update(self, otherMap)
Definition: semantics.py:468
GaudiConfig2.semantics._DictHelper.is_dirty
is_dirty
Definition: semantics.py:416
GaudiConfig2.semantics.FloatSemantics
Definition: semantics.py:143
GaudiConfig2.semantics.MappingSemantics.key_semantics
key_semantics
Definition: semantics.py:489
GaudiConfig2.semantics._ListHelper.default
default
Definition: semantics.py:299
GaudiConfig2.semantics.PropertySemantics.merge
def merge(self, a, b)
Definition: semantics.py:81
GaudiConfig2.semantics._DictHelper.items
def items(self)
Definition: semantics.py:448
GaudiConfig2.semantics._ListHelper.__setitem__
def __setitem__(self, key, value)
Definition: semantics.py:313
GaudiConfig2.semantics.DefaultSemantics.default
def default(self, value)
Definition: semantics.py:105
GaudiConfig2.semantics._DictHelper.__setitem__
def __setitem__(self, key, value)
Definition: semantics.py:430
GaudiConfig2.semantics.ComponentSemantics.default
def default(self, value)
Definition: semantics.py:264
GaudiConfig2.semantics._DictHelper.key_semantics
key_semantics
Definition: semantics.py:412
GaudiConfig2.semantics.DefaultSemantics.is_set
def is_set(self, value)
Definition: semantics.py:116
GaudiConfig2.semantics.OrderedSetSemantics.merge
def merge(self, bb, aa)
Definition: semantics.py:403
GaudiConfig2.semantics._DictHelper.data
def data(self)
Definition: semantics.py:419
GaudiConfig2.semantics.StringSemantics
Definition: semantics.py:127
GaudiConfig2.semantics._DictHelper.__getitem__
def __getitem__(self, key)
Definition: semantics.py:425
GaudiConfig2.semantics._ListHelper.__init__
def __init__(self, semantics)
Definition: semantics.py:297
GaudiConfig2.semantics.PropertySemantics.store
def store(self, value)
Definition: semantics.py:61
GaudiConfig2.semantics._ListHelper.is_dirty
is_dirty
Definition: semantics.py:301
GaudiConfig2.semantics.MappingSemantics.value_semantics
value_semantics
Definition: semantics.py:490
GaudiConfig2.semantics.PropertySemantics
Definition: semantics.py:21
GaudiConfig2.semantics.PropertySemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:73
GaudiConfig2.semantics.PropertySemantics.__init__
def __init__(self, cpp_type)
Definition: semantics.py:30
GaudiConfig2.semantics.DefaultSemantics
Definition: semantics.py:94
GaudiConfig2.semantics._ListHelper.__eq__
def __eq__(self, other)
Definition: semantics.py:322
GaudiConfig2.semantics.IntSemantics.INT_RANGES
INT_RANGES
Definition: semantics.py:158
GaudiConfig2.semantics.MappingSemantics.name
def name(self)
Definition: semantics.py:493
GaudiConfig2.semantics.SequenceSemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:378
GaudiConfig2.semantics.ComponentSemantics
Definition: semantics.py:202
GaudiConfig2.semantics._DictHelper.__init__
def __init__(self, key_semantics, value_semantics)
Definition: semantics.py:411
GaudiConfig2.semantics._DictHelper._data
_data
Definition: semantics.py:415
GaudiConfig2.semantics._ListHelper.extend
def extend(self, iterable)
Definition: semantics.py:333
GaudiConfig2.semantics.SequenceSemantics
Definition: semantics.py:344
GaudiConfig2.semantics.MappingSemantics.default
def default(self, value)
Definition: semantics.py:508
GaudiConfig2.semantics._ListHelper.opt_value
def opt_value(self)
Definition: semantics.py:337