Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v36r7 (7f57a304)
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.data
326 
327  def __ne__(self, other):
328  return self.data != other.data
329 
330  def insert(self, key, value):
331  self.is_dirty = True
332  self.data.insert(key, self.value_semantics.store(value))
333 
334  def append(self, value):
335  self.is_dirty = True
336  self.data.append(self.value_semantics.store(value))
337 
338  def extend(self, iterable):
339  self.is_dirty = True
340  self.data.extend(self.value_semantics.store(value) for value in iterable)
341 
342  def opt_value(self):
343  return [self.value_semantics.opt_value(item) for item in self.data]
344 
345  def __repr__(self):
346  return repr(self.data)
347 
348 
350  __handled_types__ = (re.compile(r"(std::)?(vector|list)<.*>$"),)
351 
352  def __init__(self, cpp_type, name=None, valueSem=None):
353  super(SequenceSemantics, self).__init__(cpp_type, name)
354  self.value_semantics = valueSem or getSemanticsFor(
355  list(extract_template_args(cpp_type))[0]
356  )
357 
358  def store(self, value):
359  new_value = _ListHelper(self.value_semantics)
360  new_value.extend(value)
361  return new_value
362 
363  def default(self, value):
364  new_value = _ListHelper(self.value_semantics)
365  new_value.default = value
366  return new_value
367 
368  def opt_value(self, value):
369  """
370  Option string version of value.
371  """
372  if not isinstance(value, _ListHelper):
373  value = self.default(value)
374  return value.opt_value()
375 
376 
378  """
379  Extend the sequence-semantics with a merge-method to behave like a
380  OrderedSet: Values are unique but the order is maintained.
381  Use 'OrderedSet<T>' as fifth parameter of the Gaudi::Property<T> constructor
382  to invoke this merging method. Also applies to std::[unordered_]set.
383  """
384 
385  __handled_types__ = (
386  re.compile(r"(std::)?(unordered_)?set<.*>$"),
387  re.compile(r"^OrderedSet<.*>$"),
388  )
389 
390  def __init__(self, cpp_type, name=None):
391  super(OrderedSetSemantics, self).__init__(cpp_type, name)
392 
393  def merge(self, bb, aa):
394  for b in bb:
395  if b not in aa:
396  aa.append(b)
397  return aa
398 
399 
400 class _DictHelper(MutableMapping):
401  def __init__(self, key_semantics, value_semantics):
402  self.key_semantics = key_semantics
403  self.value_semantics = value_semantics
404  self.default = None
405  self._data = {}
406  self.is_dirty = False
407 
408  @property
409  def data(self):
410  return self._data if self.is_dirty else self.default
411 
412  def __len__(self):
413  return len(self.data)
414 
415  def __getitem__(self, key):
416  return self.value_semantics.load(
417  self.data.__getitem__(self.key_semantics.store(key))
418  )
419 
420  def __setitem__(self, key, value):
421  self.is_dirty = True
422  self.data.__setitem__(
423  self.key_semantics.store(key), self.value_semantics.store(value)
424  )
425 
426  def __delitem__(self, key):
427  if not self.is_dirty:
428  raise RuntimeError("cannot remove elements from the default value")
429  self.data.__delitem__(self.key_semantics.store(key))
430 
431  def __iter__(self):
432  for key in self.data:
433  yield self.key_semantics.load(key)
434 
435  def keys(self):
436  return list(self)
437 
438  def items(self):
439  for key, value in self.data.items():
440  yield (self.key_semantics.load(key), self.value_semantics.load(value))
441 
442  def values(self):
443  for value in self.data.values():
444  yield self.value_semantics.load(value)
445 
446  def __contains__(self, key):
447  return self.key_semantics.store(key) in self.data
448 
449  def get(self, key, default=None):
450  key = self.key_semantics.store(key)
451  if key in self.data:
452  return self.value_semantics.load(self.data[key])
453  return default
454 
455  # __contains__, , get, __eq__, __ne__
456  # popitem, clear, setdefault
457 
458  def update(self, otherMap):
459  self.is_dirty = True
460  for key, value in otherMap.items():
461  self.data[self.key_semantics.store(key)] = self.value_semantics.store(value)
462 
463  def opt_value(self):
464  return {
465  self.key_semantics.opt_value(key): self.value_semantics.opt_value(value)
466  for key, value in self.data.items()
467  }
468 
469  def __repr__(self):
470  return repr(self.data)
471 
472 
474  __handled_types__ = (re.compile(r"(std::)?(unordered_)?map<.*>$"),)
475 
476  def __init__(self, cpp_type, name=None):
477  super(MappingSemantics, self).__init__(cpp_type, name)
478  template_args = list(extract_template_args(cpp_type))
479  self.key_semantics = getSemanticsFor(template_args[0])
480  self.value_semantics = getSemanticsFor(template_args[1])
481 
482  def store(self, value):
483  new_value = _DictHelper(self.key_semantics, self.value_semantics)
484  new_value.update(value)
485  return new_value
486 
487  def default(self, value):
488  new_value = _DictHelper(self.key_semantics, self.value_semantics)
489  new_value.default = value
490  return new_value
491 
492  def opt_value(self, value):
493  """
494  Option string version of value.
495  """
496  if not isinstance(value, _DictHelper):
497  value = self.default(value)
498  return value.opt_value()
499 
500 
501 SEMANTICS = [
502  c
503  for c in globals().values()
504  if isinstance(c, type)
505  and issubclass(c, PropertySemantics)
506  and c not in (PropertySemantics, DefaultSemantics)
507 ]
508 
509 
510 def getSemanticsFor(cpp_type, name=None):
511  for semantics in SEMANTICS:
512  try:
513  return semantics(cpp_type, name)
514  except TypeError:
515  pass
516  return DefaultSemantics(cpp_type, name)
GaudiConfig2.semantics.OrderedSetSemantics.__init__
def __init__(self, cpp_type, name=None)
Definition: semantics.py:390
GaudiConfig2.semantics.ComponentSemantics.interfaces
interfaces
Definition: semantics.py:219
GaudiConfig2.semantics.SequenceSemantics.default
def default(self, value)
Definition: semantics.py:363
GaudiConfig2.semantics.MappingSemantics.store
def store(self, value)
Definition: semantics.py:482
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:426
GaudiConfig2.semantics._ListHelper.__repr__
def __repr__(self)
Definition: semantics.py:345
GaudiConfig2.semantics.PropertySemantics.cpp_type
cpp_type
Definition: semantics.py:42
GaudiConfig2.semantics._DictHelper.__contains__
def __contains__(self, key)
Definition: semantics.py:446
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:449
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:473
GaudiConfig2.semantics._ListHelper.__getitem__
def __getitem__(self, key)
Definition: semantics.py:312
GaudiConfig2.semantics._DictHelper.value_semantics
value_semantics
Definition: semantics.py:403
GaudiConfig2.semantics.DefaultSemantics._is_set
_is_set
Definition: semantics.py:110
GaudiConfig2.semantics.OrderedSetSemantics
Definition: semantics.py:377
GaudiConfig2.semantics.StringSemantics.store
def store(self, value)
Definition: semantics.py:132
GaudiConfig2.semantics._DictHelper.keys
def keys(self)
Definition: semantics.py:435
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:510
GaudiConfig2.semantics._DictHelper.__len__
def __len__(self)
Definition: semantics.py:412
GaudiConfig2.semantics.PropertySemantics.__handled_types__
tuple __handled_types__
Definition: semantics.py:38
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:404
GaudiConfig2.semantics._ListHelper.data
data
Definition: semantics.py:325
GaudiConfig2.semantics.MappingSemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:492
GaudiConfig2.semantics._DictHelper.opt_value
def opt_value(self)
Definition: semantics.py:463
GaudiConfig2.semantics._ListHelper
Definition: semantics.py:298
GaudiConfig2.semantics.MappingSemantics.__init__
def __init__(self, cpp_type, name=None)
Definition: semantics.py:476
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:354
GaudiConfig2.semantics._ListHelper._data
_data
Definition: semantics.py:302
GaudiConfig2.semantics.DefaultSemantics._default
_default
Definition: semantics.py:109
GaudiConfig2.semantics.IntSemantics.INT_RANGES
dictionary INT_RANGES
Definition: semantics.py:160
GaudiConfig2.semantics._DictHelper.values
def values(self)
Definition: semantics.py:442
GaudiConfig2.semantics._DictHelper.__repr__
def __repr__(self)
Definition: semantics.py:469
GaudiConfig2.semantics._DictHelper
Definition: semantics.py:400
GaudiConfig2.semantics.SequenceSemantics.store
def store(self, value)
Definition: semantics.py:358
GaudiConfig2.semantics._DictHelper.__iter__
def __iter__(self)
Definition: semantics.py:431
GaudiConfig2.semantics._ListHelper.insert
def insert(self, key, value)
Definition: semantics.py:330
GaudiConfig2.semantics._ListHelper.value_semantics
value_semantics
Definition: semantics.py:300
GaudiConfig2.semantics._ListHelper.append
def append(self, value)
Definition: semantics.py:334
GaudiConfig2.semantics._DictHelper.update
def update(self, otherMap)
Definition: semantics.py:458
GaudiConfig2.semantics._DictHelper.is_dirty
is_dirty
Definition: semantics.py:406
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:479
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:438
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:420
GaudiConfig2.semantics.ComponentSemantics.default
def default(self, value)
Definition: semantics.py:266
GaudiConfig2.semantics._DictHelper.key_semantics
key_semantics
Definition: semantics.py:402
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:393
GaudiConfig2.semantics._DictHelper.data
def data(self)
Definition: semantics.py:409
GaudiConfig2.semantics.StringSemantics
Definition: semantics.py:129
GaudiConfig2.semantics._DictHelper.__getitem__
def __getitem__(self, key)
Definition: semantics.py:415
GaudiConfig2.semantics._ListHelper.__ne__
def __ne__(self, other)
Definition: semantics.py:327
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:480
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:352
GaudiConfig2.semantics.DefaultSemantics
Definition: semantics.py:96
GaudiConfig2.semantics._ListHelper.__eq__
def __eq__(self, other)
Definition: semantics.py:324
GaudiConfig2.semantics.SequenceSemantics.opt_value
def opt_value(self, value)
Definition: semantics.py:368
GaudiConfig2.semantics.ComponentSemantics
Definition: semantics.py:204
GaudiConfig2.semantics._DictHelper.__init__
def __init__(self, key_semantics, value_semantics)
Definition: semantics.py:401
GaudiConfig2.semantics._DictHelper._data
_data
Definition: semantics.py:405
GaudiConfig2.semantics._ListHelper.extend
def extend(self, iterable)
Definition: semantics.py:338
GaudiConfig2.semantics.PropertySemantics.__init__
def __init__(self, cpp_type, name=None)
Definition: semantics.py:40
GaudiConfig2.semantics.SequenceSemantics
Definition: semantics.py:349
GaudiConfig2.semantics.MappingSemantics.default
def default(self, value)
Definition: semantics.py:487
GaudiConfig2.semantics._ListHelper.opt_value
def opt_value(self)
Definition: semantics.py:342