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