The Gaudi Framework  v37r0 (b608885e)
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, name=None):
31  self._name = name
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 assignemnt 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, name=None):
215  super(ComponentSemantics, self).__init__(cpp_type, name)
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, name=None, valueSem=None):
348  super(SequenceSemantics, self).__init__(cpp_type, name)
349  self.value_semantics = valueSem or getSemanticsFor(
350  list(extract_template_args(cpp_type))[0]
351  )
352  self.value_semantics.name = name
353 
354  @property
355  def name(self):
356  return self._name
357 
358  @name.setter
359  def name(self, value):
360  self._name = value
361  self.value_semantics.name = "{} element".format(self._name)
362 
363  def store(self, value):
364  new_value = _ListHelper(self.value_semantics)
365  new_value.extend(value)
366  return new_value
367 
368  def default(self, value):
369  new_value = _ListHelper(self.value_semantics)
370  new_value.default = value
371  return new_value
372 
373  def opt_value(self, value):
374  """
375  Option string version of value.
376  """
377  if not isinstance(value, _ListHelper):
378  value = self.default(value)
379  return value.opt_value()
380 
381 
383  """
384  Extend the sequence-semantics with a merge-method to behave like a
385  OrderedSet: Values are unique but the order is maintained.
386  Use 'OrderedSet<T>' as fifth parameter of the Gaudi::Property<T> constructor
387  to invoke this merging method. Also applies to std::[unordered_]set.
388  """
389 
390  __handled_types__ = (
391  re.compile(r"(std::)?(unordered_)?set<.*>$"),
392  re.compile(r"^OrderedSet<.*>$"),
393  )
394 
395  def __init__(self, cpp_type, name=None):
396  super(OrderedSetSemantics, self).__init__(cpp_type, name)
397 
398  def merge(self, bb, aa):
399  for b in bb:
400  if b not in aa:
401  aa.append(b)
402  return aa
403 
404 
405 class _DictHelper(MutableMapping):
406  def __init__(self, key_semantics, value_semantics):
407  self.key_semantics = key_semantics
408  self.value_semantics = value_semantics
409  self.default = None
410  self._data = {}
411  self.is_dirty = False
412 
413  @property
414  def data(self):
415  return self._data if self.is_dirty else self.default
416 
417  def __len__(self):
418  return len(self.data)
419 
420  def __getitem__(self, key):
421  return self.value_semantics.load(
422  self.data.__getitem__(self.key_semantics.store(key))
423  )
424 
425  def __setitem__(self, key, value):
426  self.is_dirty = True
427  self.data.__setitem__(
428  self.key_semantics.store(key), self.value_semantics.store(value)
429  )
430 
431  def __delitem__(self, key):
432  if not self.is_dirty:
433  raise RuntimeError("cannot remove elements from the default value")
434  self.data.__delitem__(self.key_semantics.store(key))
435 
436  def __iter__(self):
437  for key in self.data:
438  yield self.key_semantics.load(key)
439 
440  def keys(self):
441  return list(self)
442 
443  def items(self):
444  for key, value in self.data.items():
445  yield (self.key_semantics.load(key), self.value_semantics.load(value))
446 
447  def values(self):
448  for value in self.data.values():
449  yield self.value_semantics.load(value)
450 
451  def __contains__(self, key):
452  return self.key_semantics.store(key) in self.data
453 
454  def get(self, key, default=None):
455  key = self.key_semantics.store(key)
456  if key in self.data:
457  return self.value_semantics.load(self.data[key])
458  return default
459 
460  # __contains__, , get, __eq__, __ne__
461  # popitem, clear, setdefault
462 
463  def update(self, otherMap):
464  self.is_dirty = True
465  for key, value in otherMap.items():
466  self.data[self.key_semantics.store(key)] = self.value_semantics.store(value)
467 
468  def opt_value(self):
469  return {
470  self.key_semantics.opt_value(key): self.value_semantics.opt_value(value)
471  for key, value in self.data.items()
472  }
473 
474  def __repr__(self):
475  return repr(self.data)
476 
477 
479  __handled_types__ = (re.compile(r"(std::)?(unordered_)?map<.*>$"),)
480 
481  def __init__(self, cpp_type, name=None):
482  super(MappingSemantics, self).__init__(cpp_type, name)
483  template_args = list(extract_template_args(cpp_type))
484  self.key_semantics = getSemanticsFor(template_args[0])
485  self.key_semantics.name = "{} key".format(name)
486  self.value_semantics = getSemanticsFor(template_args[1])
487  self.value_semantics.name = "{} value".format(name)
488 
489  @property
490  def name(self):
491  return self._name
492 
493  @name.setter
494  def name(self, value):
495  self._name = value
496  self.key_semantics.name = "{} key".format(self._name)
497  self.value_semantics.name = "{} value".format(self._name)
498 
499  def store(self, value):
500  new_value = _DictHelper(self.key_semantics, self.value_semantics)
501  new_value.update(value)
502  return new_value
503 
504  def default(self, value):
505  new_value = _DictHelper(self.key_semantics, self.value_semantics)
506  new_value.default = value
507  return new_value
508 
509  def opt_value(self, value):
510  """
511  Option string version of value.
512  """
513  if not isinstance(value, _DictHelper):
514  value = self.default(value)
515  return value.opt_value()
516 
517 
518 SEMANTICS = [
519  c
520  for c in globals().values()
521  if isinstance(c, type)
522  and issubclass(c, PropertySemantics)
523  and c not in (PropertySemantics, DefaultSemantics)
524 ]
525 
526 
527 def getSemanticsFor(cpp_type, name=None):
528  for semantics in SEMANTICS:
529  try:
530  return semantics(cpp_type, name)
531  except TypeError:
532  pass
533  return DefaultSemantics(cpp_type, name)
GaudiConfig2.semantics.OrderedSetSemantics.__init__
def __init__(self, cpp_type, name=None)
Definition: semantics.py:395
GaudiConfig2.semantics.ComponentSemantics.interfaces
interfaces
Definition: semantics.py:217
GaudiConfig2.semantics.SequenceSemantics.default
def default(self, value)
Definition: semantics.py:368
GaudiConfig2.semantics.MappingSemantics.store
def store(self, value)
Definition: semantics.py:499
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:431
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:451
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:454
GaudiConfig2.semantics.DefaultSemantics.store
def store(self, value)
Definition: semantics.py:111
GaudiConfig2.semantics.MappingSemantics
Definition: semantics.py:478
GaudiConfig2.semantics._ListHelper.__getitem__
def __getitem__(self, key)
Definition: semantics.py:310
GaudiConfig2.semantics._DictHelper.value_semantics
value_semantics
Definition: semantics.py:408
GaudiConfig2.semantics.DefaultSemantics._is_set
_is_set
Definition: semantics.py:108
GaudiConfig2.semantics.OrderedSetSemantics
Definition: semantics.py:382
GaudiConfig2.semantics.StringSemantics.store
def store(self, value)
Definition: semantics.py:130
GaudiConfig2.semantics._DictHelper.keys
def keys(self)
Definition: semantics.py:440
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.getSemanticsFor
def getSemanticsFor(cpp_type, name=None)
Definition: semantics.py:527
GaudiConfig2.semantics.PropertySemantics._name
_name
Definition: semantics.py:31
GaudiConfig2.semantics._DictHelper.__len__
def __len__(self)
Definition: semantics.py:417
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:409
GaudiConfig2.semantics._ListHelper.data
data
Definition: semantics.py:323
GaudiConfig2.semantics.MappingSemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:509
GaudiConfig2.semantics._DictHelper.opt_value
def opt_value(self)
Definition: semantics.py:468
GaudiConfig2.semantics.PropertySemantics.name
def name(self)
Definition: semantics.py:35
GaudiConfig2.semantics._ListHelper
Definition: semantics.py:296
GaudiConfig2.semantics.MappingSemantics.__init__
def __init__(self, cpp_type, name=None)
Definition: semantics.py:481
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:355
GaudiConfig2.semantics._DictHelper.values
def values(self)
Definition: semantics.py:447
GaudiConfig2.semantics._DictHelper.__repr__
def __repr__(self)
Definition: semantics.py:474
GaudiConfig2.semantics._DictHelper
Definition: semantics.py:405
GaudiConfig2.semantics.SequenceSemantics.store
def store(self, value)
Definition: semantics.py:363
GaudiConfig2.semantics._DictHelper.__iter__
def __iter__(self)
Definition: semantics.py:436
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:463
GaudiConfig2.semantics._DictHelper.is_dirty
is_dirty
Definition: semantics.py:411
GaudiConfig2.semantics.FloatSemantics
Definition: semantics.py:143
GaudiConfig2.semantics.ComponentSemantics.__init__
def __init__(self, cpp_type, name=None)
Definition: semantics.py:214
GaudiConfig2.semantics.MappingSemantics.key_semantics
key_semantics
Definition: semantics.py:484
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:443
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:425
GaudiConfig2.semantics.ComponentSemantics.default
def default(self, value)
Definition: semantics.py:264
GaudiConfig2.semantics._DictHelper.key_semantics
key_semantics
Definition: semantics.py:407
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:398
GaudiConfig2.semantics._DictHelper.data
def data(self)
Definition: semantics.py:414
GaudiConfig2.semantics.StringSemantics
Definition: semantics.py:127
GaudiConfig2.semantics._DictHelper.__getitem__
def __getitem__(self, key)
Definition: semantics.py:420
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:486
GaudiConfig2.semantics.PropertySemantics
Definition: semantics.py:21
GaudiConfig2.semantics.PropertySemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:73
GaudiConfig2.semantics.SequenceSemantics.__init__
def __init__(self, cpp_type, name=None, valueSem=None)
Definition: semantics.py:347
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:490
GaudiConfig2.semantics.SequenceSemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:373
GaudiConfig2.semantics.ComponentSemantics
Definition: semantics.py:202
GaudiConfig2.semantics._DictHelper.__init__
def __init__(self, key_semantics, value_semantics)
Definition: semantics.py:406
GaudiConfig2.semantics._DictHelper._data
_data
Definition: semantics.py:410
GaudiConfig2.semantics._ListHelper.extend
def extend(self, iterable)
Definition: semantics.py:333
GaudiConfig2.semantics.PropertySemantics.__init__
def __init__(self, cpp_type, name=None)
Definition: semantics.py:30
GaudiConfig2.semantics.SequenceSemantics
Definition: semantics.py:344
GaudiConfig2.semantics.MappingSemantics.default
def default(self, value)
Definition: semantics.py:504
GaudiConfig2.semantics._ListHelper.opt_value
def opt_value(self)
Definition: semantics.py:337