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