The Gaudi Framework  v33r0 (d5ea422b)
semantics.py
Go to the documentation of this file.
1 
11 from __future__ import absolute_import
12 import sys
13 import re
14 import logging
15 try:
16  from collections.abc import MutableSequence, MutableMapping
17 except ImportError: # pragma no cover
18  # Python 2 version
19  from collections import MutableSequence, MutableMapping
20 
21 if sys.version_info >= (3, ): # pragma no cover
22  basestring = str
23 
24 _log = logging.getLogger(__name__)
25 is_64bits = sys.maxsize > 2**32
26 
27 
28 class PropertySemantics(object):
29  '''
30  Basic property semantics implementation, with no validation/transformation.
31  '''
32  __handled_types__ = (re.compile(r'.*'), )
33 
34  def __init__(self, cpp_type, name=None):
35  self.name = None
36  self.cpp_type = cpp_type
37 
38  @property
39  def cpp_type(self):
40  return self._cpp_type
41 
42  @cpp_type.setter
43  def cpp_type(self, value):
44  if not any(
45  h.match(value) if hasattr(h, 'match') else h == value
46  for h in self.__handled_types__):
47  raise TypeError('C++ type {!r} not supported'.format(value))
48  self._cpp_type = value
49 
50  def load(self, value):
51  '''
52  Transformation for data when reading the property.
53  '''
54  return value
55 
56  def store(self, value):
57  '''
58  Validation/transformation of the data to be stored.
59  '''
60  return value
61 
62  def is_set(self, value):
63  '''
64  Allow overriding the definition of "is set" if we need helper types.
65  '''
66  return True
67 
68  def opt_value(self, value):
69  '''
70  Option string version of value.
71  '''
72  if hasattr(value, '__opt_value__'):
73  return value.__opt_value__()
74  return value
75 
76  def merge(self, a, b):
77  '''
78  "Merge" two values.
79 
80  Used when merging two Configurable instances, by default just ensure
81  the two values do not conflict, but it can be overridden in
82  derived semantics to, for example, append to the two lists.
83  '''
84  if self.store(a) != self.store(b):
85  raise ValueError('cannot merge values %r and %r' % (a, b))
86  return a
87 
88 
89 DefaultSemantics = PropertySemantics
90 
91 
93  __handled_types__ = ('std::string', )
94 
95  def store(self, value):
96  if not isinstance(value, basestring):
97  raise ValueError('cannot set property {} to {!r}'.format(
98  self.name, value))
99  return value
100 
101 
103  __handled_types__ = ('bool', )
104 
105  def store(self, value):
106  return bool(value)
107 
108 
110  __handled_types__ = ('float', 'double')
111 
112  def store(self, value):
113  from numbers import Number
114  if not isinstance(value, Number):
115  raise TypeError(
116  'number expected, got {!r} in assignemnt to {}'.format(
117  value, self.name))
118  return float(value)
119 
120 
122  # dictionary generated with tools/print_limits.cpp
123  INT_RANGES = {
124  'signed char': (-128, 127),
125  'short': (-32768, 32767),
126  'int': (-2147483648, 2147483647),
127  'long': ((-9223372036854775808, 9223372036854775807) if is_64bits else
128  (-2147483648, 2147483647)),
129  'long long': (-9223372036854775808, 9223372036854775807),
130  'unsigned char': (0, 255),
131  'unsigned short': (0, 65535),
132  'unsigned int': (0, 4294967295),
133  'unsigned long': (0,
134  18446744073709551615 if is_64bits else 4294967295),
135  'unsigned long long': (0, 18446744073709551615),
136  }
137 
138  __handled_types__ = tuple(INT_RANGES)
139 
140  def store(self, value):
141  from numbers import Number
142  if not isinstance(value, Number):
143  raise TypeError(
144  'number expected, got {!r} in assignemnt to {}'.format(
145  value, self.name))
146  v = int(value)
147  if v != value:
148  _log.warning('converted %s to %d in assignment to %s', value, v,
149  self.name)
150  min_value, max_value = self.INT_RANGES[self.cpp_type]
151  if v < min_value or v > max_value:
152  raise ValueError('value {} outside limits for {!r} {}'.format(
153  v, self.cpp_type, self.INT_RANGES[self.cpp_type]))
154  return v
155 
156 
158  __handled_types__ = ('Algorithm', 'Auditor',
159  re.compile(r'AlgTool(:I[a-zA-Z0-9_]*)*$'),
160  re.compile(r'Service(:I[a-zA-Z0-9_]*)*$'))
161 
162  def __init__(self, cpp_type, name=None):
163  super(ComponentSemantics, self).__init__(cpp_type, name)
164  self.interfaces = cpp_type.split(':')
165  self.cpp_type = self.interfaces.pop(0)
166  self.interfaces = set(self.interfaces)
167 
168  def store(self, value):
169  from . import Configurable, Configurables
170  if isinstance(value, Configurable):
171  value.name # make sure the configurable has a name
172  elif isinstance(value, basestring):
173  # try to map the sring to an existing Configurable
174  if value in Configurable.instances:
175  value = Configurable.instances[value]
176  else:
177  # or create one from type and name
178  if '/' in value:
179  t, n = value.split('/')
180  else:
181  t = n = value
182  value = Configurables.getByType(t).getInstance(n)
183  else:
184  raise TypeError(
185  'cannot assign {!r} to {!r}, requested string or {!r}'.format(
186  value, self.name, self.cpp_type))
187  if value.__component_type__ != self.cpp_type:
188  raise TypeError(
189  'wrong type for {!r}: expected {!r}, got {!r}'.format(
190  self.name, self.cpp_type, value.__component_type__))
191  try:
192  # if no interface is declared we cannot check
193  if value.__interfaces__:
194  if not self.interfaces.issubset(value.__interfaces__):
195  raise TypeError(
196  'wrong interfaces for {!r}: required {}'.format(
197  self.name, list(self.interfaces)))
198  except AttributeError:
199  pass # no interfaces declared by the configrable, cannot check
200  return value
201 
202  def default(self, value):
203  return self.store(value)
204 
205 
206 def extract_template_args(cpp_type):
207  '''
208  Return an iterator over the list of template arguments in a C++ type
209  string.
210 
211  >>> t = 'map<string, vector<int, allocator<int> >, allocator<v<i>, a<i>> >'
212  >>> list(extract_template_args(t))
213  ['string', 'vector<int, allocator<int> >', 'allocator<v<i>, a<i>>']
214  >>> list(extract_template_args('int'))
215  []
216  '''
217  template_level = 0
218  arg_start = -1
219  for p, c in enumerate(cpp_type):
220  if c == ',':
221  if template_level == 1:
222  yield cpp_type[arg_start:p].strip()
223  arg_start = p + 1
224  elif c == '<':
225  template_level += 1
226  if template_level == 1:
227  arg_start = p + 1
228  elif c == '>':
229  template_level -= 1
230  if template_level == 0:
231  yield cpp_type[arg_start:p].strip()
232 
233 
234 class _ListHelper(MutableSequence):
235  def __init__(self, semantics):
236  self.value_semantics = semantics
237  self.default = None
238  self._data = []
239  self.is_dirty = False
240 
241  @property
242  def data(self):
243  return self._data if self.is_dirty else self.default
244 
245  def __len__(self):
246  return len(self.data)
247 
248  def __getitem__(self, key):
249  return self.value_semantics.load(self.data.__getitem__(key))
250 
251  def __setitem__(self, key, value):
252  self.is_dirty = True
253  self.data.__setitem__(key, self.value_semantics.store(value))
254 
255  def __delitem__(self, key):
256  if not self.is_dirty:
257  raise RuntimeError('cannot remove elements from the default value')
258  self.data.__delitem__(key)
259 
260  def insert(self, key, value):
261  self.is_dirty = True
262  self.data.insert(key, self.value_semantics.store(value))
263 
264  def append(self, value):
265  self.is_dirty = True
266  self.data.append(self.value_semantics.store(value))
267 
268  def extend(self, iterable):
269  self.is_dirty = True
270  self.data.extend(
271  self.value_semantics.store(value) for value in iterable)
272 
273  def opt_value(self):
274  return [self.value_semantics.opt_value(item) for item in self.data]
275 
276  def __repr__(self):
277  return repr(self.data)
278 
279 
281  __handled_types__ = (re.compile(r'(std::)?(vector|list)<.*>$'), )
282 
283  def __init__(self, cpp_type, name=None):
284  super(SequenceSemantics, self).__init__(cpp_type, name)
286  list(extract_template_args(cpp_type))[0])
287 
288  def store(self, value):
289  new_value = _ListHelper(self.value_semantics)
290  new_value.extend(value)
291  return new_value
292 
293  def default(self, value):
294  new_value = _ListHelper(self.value_semantics)
295  new_value.default = value
296  return new_value
297 
298  def opt_value(self, value):
299  '''
300  Option string version of value.
301  '''
302  if not isinstance(value, _ListHelper):
303  value = self.default(value)
304  return value.opt_value()
305 
306 
307 class _DictHelper(MutableMapping):
308  def __init__(self, key_semantics, value_semantics):
309  self.key_semantics = key_semantics
310  self.value_semantics = value_semantics
311  self.default = None
312  self._data = {}
313  self.is_dirty = False
314 
315  @property
316  def data(self):
317  return self._data if self.is_dirty else self.default
318 
319  def __len__(self):
320  return len(self.data)
321 
322  def __getitem__(self, key):
323  return self.value_semantics.load(
324  self.data.__getitem__(self.key_semantics.store(key)))
325 
326  def __setitem__(self, key, value):
327  self.is_dirty = True
328  self.data.__setitem__(
329  self.key_semantics.store(key), self.value_semantics.store(value))
330 
331  def __delitem__(self, key):
332  if not self.is_dirty:
333  raise RuntimeError('cannot remove elements from the default value')
334  self.data.__delitem__(self.key_semantics.store(key))
335 
336  def __iter__(self):
337  for key in self.data:
338  yield self.key_semantics.load(key)
339 
340  def keys(self):
341  return list(self)
342 
343  def items(self):
344  for key, value in self.data.items():
345  yield (self.key_semantics.load(key),
346  self.value_semantics.load(value))
347 
348  def values(self):
349  for value in self.data.values():
350  yield self.value_semantics.load(value)
351 
352  def __contains__(self, key):
353  return self.key_semantics.store(key) in self.data
354 
355  def get(self, key, default=None):
356  key = self.key_semantics.store(key)
357  if key in self.data:
358  return self.value_semantics.load(self.data[key])
359  return default
360 
361  # __contains__, , get, __eq__, __ne__
362  # popitem, clear, update, setdefault
363 
364  def opt_value(self):
365  return {
366  self.key_semantics.opt_value(key):
367  self.value_semantics.opt_value(value)
368  for key, value in self.data.items()
369  }
370 
371  def __repr__(self):
372  return repr(self.data)
373 
374 
376  __handled_types__ = (re.compile(r'(std::)?(unordered_)?map<.*>$'), )
377 
378  def __init__(self, cpp_type, name=None):
379  super(MappingSemantics, self).__init__(cpp_type, name)
380  template_args = list(extract_template_args(cpp_type))
381  self.key_semantics = getSemanticsFor(template_args[0])
382  self.value_semantics = getSemanticsFor(template_args[1])
383 
384  def store(self, value):
385  new_value = _DictHelper(self.key_semantics, self.value_semantics)
386  new_value.update(value)
387  return new_value
388 
389  def default(self, value):
390  new_value = _DictHelper(self.key_semantics, self.value_semantics)
391  new_value.default = value
392  return new_value
393 
394  def opt_value(self, value):
395  '''
396  Option string version of value.
397  '''
398  if not isinstance(value, _DictHelper):
399  value = self.default(value)
400  return value.opt_value()
401 
402 
403 SEMANTICS = [
404  c for c in globals().values()
405  if isinstance(c, type) and PropertySemantics in c.__bases__
406 ]
407 
408 
409 def getSemanticsFor(cpp_type, name=None):
410  for semantics in SEMANTICS:
411  try:
412  return semantics(cpp_type, name)
413  except TypeError:
414  pass
415  return DefaultSemantics(cpp_type, name)
def __init__(self, cpp_type, name=None)
Definition: semantics.py:34
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
def getSemanticsFor(cpp_type, name=None)
Definition: semantics.py:409
def get(self, key, default=None)
Definition: semantics.py:355
def __setitem__(self, key, value)
Definition: semantics.py:326
def __init__(self, cpp_type, name=None)
Definition: semantics.py:162
def insert(self, key, value)
Definition: semantics.py:260
def __init__(self, cpp_type, name=None)
Definition: semantics.py:283
def __init__(self, semantics)
Definition: semantics.py:235
def extend(self, iterable)
Definition: semantics.py:268
def extract_template_args(cpp_type)
Definition: semantics.py:206
def __setitem__(self, key, value)
Definition: semantics.py:251
def __init__(self, cpp_type, name=None)
Definition: semantics.py:378
def __init__(self, key_semantics, value_semantics)
Definition: semantics.py:308