The Gaudi Framework  v36r1 (3e2fb5a8)
PropertyProxy.py
Go to the documentation of this file.
1 
14 
15 # data ---------------------------------------------------------------------
16 __all__ = [
17  'PropertyProxy', 'GaudiHandlePropertyProxy',
18  'GaudiHandleArrayPropertyProxy'
19 ]
20 
21 import os
22 from GaudiKernel.GaudiHandles import *
23 from GaudiKernel import ConfigurableDb
24 from GaudiKernel.DataHandle import DataHandle
25 
26 import logging
27 log = logging.getLogger('PropertyProxy')
28 
29 
30 def derives_from(derived, base):
31  """A string version of isinstance().
32  <derived> is either an object instance, or a type
33  <base> is a string containing the name of the base class (or <derived> class)"""
34  if not isinstance(derived, type):
35  derived = type(derived)
36  if derived.__name__ == base:
37  return True
38  for b in derived.__bases__:
39  if derives_from(b, base):
40  return True
41 
42  return False
43 
44 
45 def _isCompatible(tp, value):
46  errmsg = "received an instance of %s, but %s expected" % (type(value), tp)
47 
48  if derives_from(value, 'PropertyReference'):
49  # TODO: implement type checking for references
50  return value # references are valid
51  if (tp is str):
52  if (type(value) is str) or derives_from(value, 'Configurable'):
53  # we can set string properties only from strings or configurables
54  return value
55  elif isinstance(value, DataHandle):
56  # Implicitly convert DataHandle to str for backward
57  # compatiblity in cases like B.Input (str) = A.Output (handle)
58  return str(value)
59  else:
60  raise ValueError(errmsg)
61  elif (tp in [list, tuple, dict]):
62  if (type(value) is tp):
63  # We need to check that the types match for lists, tuples and
64  # dictionaries (bug #34769).
65  return value
66  else:
67  raise ValueError(errmsg)
68  elif derives_from(tp, 'Configurable'):
69  return value
70  else:
71  # all other types: accept if conversion allowed
72  try:
73  dummy = tp(value)
74  except (TypeError, ValueError):
75  raise ValueError(errmsg)
76 
77  return dummy # in case of e.g. classes with __int__, __iter__, etc. implemented
78 
79 
80 class PropertyProxy(object):
81  def __init__(self, descr, docString=None, default=None):
82  self.history = {}
83  self.descr = descr
84  self.deprecated = False
85  if docString:
86  self.__doc__ = docString
87  if '[[deprecated]]' in docString:
88  self.deprecated = True
89  if default is not None:
90  self.default = default
91 
92  def setDefault(self, value):
93  self.__default = value
94 
95  def getDefault(self):
96  return self.__default
97 
98  default = property(getDefault, setDefault)
99 
100  def fullPropertyName(self, obj):
101  return (obj.getJobOptName()
102  or obj.getName()) + '.' + self.descr.__name__
103 
104  def __get__(self, obj, type=None):
105  try:
106  return self.descr.__get__(obj, type)
107  except AttributeError:
108  # special case for lists and dictionaries:
109  # allow default to work with on += and []
110  if self.__default.__class__ in [list, dict]:
111  self.descr.__set__(obj,
112  self.__default.__class__(self.__default))
113  return self.descr.__get__(obj, type)
114  else:
115  # for non lists (or dicts) return a reference to the default
116  # return self.__default
117  raise
118 
119  def __set__(self, obj, value):
120  # check if deprecated
121  if self.deprecated and not obj._unpickling:
122  log.warning('Property %s is deprecated: %s',
123  self.fullPropertyName(obj), self.__doc__)
124 
125  # check value/property compatibility if possible
126  proptype, allowcompat = None, False
127  if hasattr(self, 'default'):
128  proptype = type(self.default)
129  if self.descr.__name__ == 'OutputLevel': # old-style compat for Btag
130  allowcompat = True
131  elif obj in self.history:
132  proptype = type(self.history[obj][0])
133  allowcompat = True
134 
135  # check if type known; allow special initializer for typed instances
136  # Do not perform the check for PropertyReference, should be delayed until
137  # binding (if ever done)
138  if proptype and proptype != type(None) and \
139  not derives_from(value, 'PropertyReference'):
140  try:
141  # check value itself
142  value = _isCompatible(proptype, value)
143 
144  # check element in case of list
145  if proptype == list:
146  try:
147  oldvec = self.descr.__get__(obj, type)
148  if oldvec:
149  tpo = type(oldvec[0])
150  for v in value:
151  _isCompatible(tpo, v)
152  except AttributeError:
153  # value not yet set
154  pass
155  except ValueError as e:
156  if allowcompat:
157  log.error('inconsistent value types for %s.%s (%s)' %
158  (obj.getName(), self.descr.__name__, str(e)))
159  else:
160  raise
161 
162  # allow a property to be set if we're in non-default mode, or if it
163  # simply hasn't been set before
164  if not obj._isInSetDefaults() or not obj in self.history:
165  # by convention, 'None' for default is used to designate objects setting
166  if hasattr(self, 'default') and self.default == None:
167  obj.__iadd__(value, self.descr) # to establish hierarchy
168  else:
169  self.descr.__set__(obj, value)
170  self.history.setdefault(obj, []).append(value)
171 
172  def __delete__(self, obj):
173  if obj in self.history:
174  del self.history[obj]
175  self.descr.__delete__(obj)
176 
177 
179  """A class with some utilities for GaudiHandles and GaudiHandleArrays"""
180 
181  def __init__(self, descr, docString, default, handleType, allowedType):
182  """<descr>: the real property in the object instance (from __slots__)
183  <docString>: the documentation string of this property
184  <default>: default value from C++ (via python generated by genconf)
185  <handleType>: real python handle type (e.g. PublicToolHandle, PrivateToolHandle, ...)
186  <allowedType>: allowed instance type for default
187  """
188  # check that default is of allowed type for this proxy
189  if not isinstance(default, allowedType):
190  raise TypeError("%s: %s default: %r is not a %s" %
191  (descr.__name__, self.__class__.__name__, default,
192  allowedType.__name__))
193  PropertyProxy.__init__(self, descr, docString, default)
194  self._handleType = handleType
195  self._confTypeName = 'Configurable' + handleType.componentType
196 # print "%s: %r (%s)" % (self.__class__.__name__,self._handleType,self._confTypeName)
197 
198  def __get__(self, obj, type=None):
199  try:
200  return self.descr.__get__(obj, type)
201  except AttributeError:
202  # Get default
203  try:
204  default = obj.__class__.getDefaultProperty(self.descr.__name__)
205  default = self.convertDefaultToBeSet(obj, default)
206  if default:
207  self.__set__(obj, default)
208  except AttributeError as e:
209  # change type of exception to avoid false error message
210  raise RuntimeError(*e.args)
211 
212  return self.descr.__get__(obj, type)
213 
214  def __set__(self, obj, value):
215  # allow a property to be set if we're in non-default mode, or if it
216  # simply hasn't been set before
217  if not obj._isInSetDefaults() or not obj in self.history:
218  value = self.convertValueToBeSet(obj, value)
219  # assign the value
220  self.descr.__set__(obj, value)
221  log.debug("Setting %s = %r", self.fullPropertyName(obj), value)
222  self.history.setdefault(obj, []).append(value)
223 
224  def isHandle(self, value):
225  """Check if <value> is a handle of the correct type"""
226  return isinstance(value, self._handleType)
227 
228  def isConfig(self, value):
229  """Check if <value> is a configurable of the correct type"""
230  return derives_from(value, self._confTypeName)
231 
232  def getDefaultConfigurable(self, typeAndName, requester):
233  """Return the configurable instance corresponding to the toolhandle if possible.
234  Otherwise return None"""
235  global log
236  # find the module
237  typeAndNameTuple = typeAndName.split('/')
238  confType = typeAndNameTuple[0]
239  confClass = ConfigurableDb.getConfigurable(confType)
240  # check the type of the configurable
241  if not derives_from(confClass, self._confTypeName):
242  log.error("%s: Configurable %s is not a %s", requester, confType,
243  self._confTypeName)
244  return None
245  try:
246  confName = typeAndNameTuple[1]
247  except IndexError:
248  return confClass() # use default name
249  else:
250  return confClass(confName)
251 
252  def convertDefaultToBeSet(self, obj, default):
253  # turn string into handle
254  isString = type(default) == str
255  if not isString and self.isConfig(default):
256  # print self.fullPropertyName(obj) + ": Setting default configurable: %r" % default
257  return default
258  elif isString or self.isHandle(default):
259  if isString:
260  # convert string into handle
261  typeAndName = default
262  default = self._handleType(typeAndName)
263  else:
264  typeAndName = default.typeAndName
265  if not self._handleType.isPublic:
266  if not typeAndName:
267  return None
268  # Find corresponding default configurable of private handles
269  # (make sure the name used to instantiate the private tool
270  # includes the name of the owner, see https://gitlab.cern.ch/gaudi/Gaudi/-/issues/141)
271  if '/' in typeAndName:
272  typeAndName = typeAndName.replace(
273  '/', '/{}.'.format(obj.name()), 1)
274  else:
275  typeAndName = "{0}/{1}.{0}".format(typeAndName, obj.name())
276  try:
277  conf = self.getDefaultConfigurable(
278  typeAndName, self.fullPropertyName(obj))
279 
280 
281 # print self.fullPropertyName(obj) + ": Setting default private configurable (from default handle): %r" % conf
282  except AttributeError as e:
283  # change type of exception to avoid false error message
284  raise RuntimeError(*e.args)
285  if conf is None:
286  raise RuntimeError(
287  "%s: Default configurable for class %s not found in ConfigurableDb.CfgDb"
288  % (self.fullPropertyName(obj), default.getType()))
289  return conf
290  else: # not a config, not a handle, not a string
291  raise TypeError("%s: default value %r is not of type %s or %s" %
292  (self.fullPropertyName(obj), default,
293  self._confTypeName, self._handleType.__name__))
294 
295  return default
296 
297  def convertValueToBeSet(self, obj, value):
298  if value is None:
299  value = ''
300  isString = type(value) == str
301  if isString:
302  # create an new handle
303  return self._handleType(value)
304  elif self.isHandle(value):
305  # make a copy of the handle
306  return self._handleType(value.toStringProperty())
307  elif self.isConfig(value):
308  if self._handleType.isPublic:
309  # A public tool must be registered to ToolSvc before assigning it
310  if derives_from(value, 'ConfigurableAlgTool'):
311  if not value.isInToolSvc():
312  suggestion = 'You may need to add jobOptions lines something like:' + os.linesep + \
313  'from AthenaCommon.AppMgr import ToolSvc' + os.linesep + \
314  'ToolSvc += '
315  if value.getName() == value.getType(
316  ): # using default name
317  suggestion += '%s()' % value.__class__.__name__
318  else: # using user-defined name
319  suggestion += '%s(%r)' % (value.__class__.__name__,
320  value.getName())
321  raise RuntimeError(
322  self.fullPropertyName(obj) +
323  ': Public tool %s is not yet in ToolSvc. %s' %
324  (value.getJobOptName(), suggestion))
325  # make it read-only
326  return self._handleType(value.toStringProperty())
327  elif value.hasParent(obj.getJobOptName()):
328  # is already a child, keep as-is
329  return value
330  else:
331  # make a copy of the configurable
332  value = obj.copyChildAndSetParent(value, obj.getJobOptName())
333  # ensure that the new object is in allConfigurables
334  obj.allConfigurables[value.name()] = value
335  return value
336  else:
337  raise TypeError(
338  "Property %s value %r is not a %s nor a %s nor a string" %
339  (self.fullPropertyName(obj), value, self._confTypeName,
340  self._handleType.__name__))
341 
342  return value
343 
344 
346  def __init__(self, descr, docString, default):
347  GaudiHandlePropertyProxyBase.__init__(self, descr, docString, default,
348  type(default), GaudiHandle)
349 
350 
352  def __init__(self, descr, docString, default):
353  """<descr>: the real property in the object instance (from __slots__)
354  <confTypeName>: string indicating the (base) class of allowed Configurables to be assigned.
355  <handleType>: real python handle type (e.g. PublicToolHandle, PrivateToolHandle, ...)
356  """
357  GaudiHandlePropertyProxyBase.__init__(self, descr, docString, default,
358  type(default).handleType,
359  GaudiHandleArray)
360  self.arrayType = type(default)
361 
362  def checkType(self, obj, value):
363  if not isinstance(value, list) and not isinstance(
364  value, self.arrayType):
365  raise TypeError(
366  "%s: Value %r is not a list nor a %s" %
367  (self.fullPropertyName(obj), value, self.arrayType.__name__))
368 
369  def convertDefaultToBeSet(self, obj, default):
370  self.checkType(obj, default)
371  newDefault = self.arrayType()
372  for d in default:
373  cd = GaudiHandlePropertyProxyBase.convertDefaultToBeSet(
374  self, obj, d)
375  if cd:
376  newDefault.append(cd)
377 
378  return newDefault
379 
380  def convertValueToBeSet(self, obj, value):
381  self.checkType(obj, value)
382  newValue = self.arrayType()
383  for v in value:
384  cv = GaudiHandlePropertyProxyBase.convertValueToBeSet(self, obj, v)
385  if cv:
386  newValue.append(cv)
387 
388  return newValue
389 
390 
392  def __init__(self, descr, docString, default):
393  PropertyProxy.__init__(self, descr, docString, default)
394 
395  def __get__(self, obj, type=None):
396  try:
397  return self.descr.__get__(obj, type)
398  except AttributeError:
399  # Get default
400  try:
401  default = obj.__class__.getDefaultProperty(self.descr.__name__)
402  default = self.convertValueToBeSet(obj, default)
403  if default:
404  self.__set__(obj, default)
405  except AttributeError as e:
406  # change type of exception to avoid false error message
407  raise RuntimeError(*e.args)
408 
409  return self.descr.__get__(obj, type)
410 
411  def __set__(self, obj, value):
412  if not obj._isInSetDefaults() or not obj in self.history:
413  value = self.convertValueToBeSet(obj, value)
414  # assign the value
415  self.descr.__set__(obj, value)
416  log.debug("Setting %s = %r", self.fullPropertyName(obj), value)
417  self.history.setdefault(obj, []).append(value)
418 
419  def convertValueToBeSet(self, obj, value):
420  if value is None:
421  value = ''
422 
423  mode = obj.__class__.getDefaultProperty(self.descr.__name__).mode()
424  _type = obj.__class__.getDefaultProperty(self.descr.__name__).type()
425  if type(value) == str:
426  return DataHandle(value, mode, _type)
427  elif isinstance(value, DataHandle):
428  return DataHandle(value.__str__(), mode, _type)
429  else:
430  raise ValueError("received an instance of %s, but %s expected" %
431  (type(value), 'str or DataHandle'))
432 
433 
434 def PropertyProxyFactory(descr, doc, default):
435  # print "PropertyProxyFactory( %s, %r )" % (descr.__name__,default)
436 
437  if isinstance(default, GaudiHandleArray):
438  return GaudiHandleArrayPropertyProxy(descr, doc, default)
439 
440  if isinstance(default, GaudiHandle):
441  return GaudiHandlePropertyProxy(descr, doc, default)
442 
443  if isinstance(default, DataHandle):
444  return DataHandlePropertyProxy(descr, doc, default)
445 
446  return PropertyProxy(descr, doc, default)
GaudiKernel.PropertyProxy.PropertyProxy.__get__
def __get__(self, obj, type=None)
Definition: PropertyProxy.py:104
GaudiKernel.PropertyProxy.PropertyProxy.__default
__default
Definition: PropertyProxy.py:93
GaudiKernel.PropertyProxy.PropertyProxy.setDefault
def setDefault(self, value)
Definition: PropertyProxy.py:92
GaudiKernel.PropertyProxy.GaudiHandleArrayPropertyProxy.convertValueToBeSet
def convertValueToBeSet(self, obj, value)
Definition: PropertyProxy.py:380
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.isHandle
def isHandle(self, value)
Definition: PropertyProxy.py:224
GaudiKernel.PropertyProxy.PropertyProxy.getDefault
def getDefault(self)
Definition: PropertyProxy.py:95
GaudiKernel.PropertyProxy.GaudiHandleArrayPropertyProxy
Definition: PropertyProxy.py:351
GaudiKernel.PropertyProxy.DataHandlePropertyProxy.__set__
def __set__(self, obj, value)
Definition: PropertyProxy.py:411
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.convertDefaultToBeSet
def convertDefaultToBeSet(self, obj, default)
Definition: PropertyProxy.py:252
GaudiKernel.DataHandle.DataHandle
Definition: DataHandle.py:14
compareRootHistos.tp
tuple tp
Definition: compareRootHistos.py:461
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.isConfig
def isConfig(self, value)
Definition: PropertyProxy.py:228
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxy
Definition: PropertyProxy.py:345
GaudiKernel.PropertyProxy.DataHandlePropertyProxy
Definition: PropertyProxy.py:391
GaudiKernel.PropertyProxy._isCompatible
def _isCompatible(tp, value)
Definition: PropertyProxy.py:45
GaudiKernel.DataHandle
Definition: DataHandle.py:1
GaudiKernel.PropertyProxy.PropertyProxy.deprecated
deprecated
Definition: PropertyProxy.py:84
GaudiKernel.PropertyProxy.DataHandlePropertyProxy.__get__
def __get__(self, obj, type=None)
Definition: PropertyProxy.py:395
GaudiKernel.PropertyProxy.derives_from
def derives_from(derived, base)
Definition: PropertyProxy.py:30
GaudiKernel.PropertyProxy.PropertyProxy.__doc__
__doc__
Definition: PropertyProxy.py:86
GaudiKernel.PropertyProxy.PropertyProxyFactory
def PropertyProxyFactory(descr, doc, default)
Definition: PropertyProxy.py:434
GaudiKernel.PropertyProxy.PropertyProxy.descr
descr
Definition: PropertyProxy.py:83
GaudiKernel.PropertyProxy.GaudiHandleArrayPropertyProxy.__init__
def __init__(self, descr, docString, default)
Definition: PropertyProxy.py:352
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxy.__init__
def __init__(self, descr, docString, default)
Definition: PropertyProxy.py:346
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.convertValueToBeSet
def convertValueToBeSet(self, obj, value)
Definition: PropertyProxy.py:297
GaudiKernel.PropertyProxy.PropertyProxy.fullPropertyName
def fullPropertyName(self, obj)
Definition: PropertyProxy.py:100
GaudiKernel.PropertyProxy.DataHandlePropertyProxy.__init__
def __init__(self, descr, docString, default)
Definition: PropertyProxy.py:392
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase
Definition: PropertyProxy.py:178
GaudiKernel.PropertyProxy.PropertyProxy
Definition: PropertyProxy.py:80
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.__init__
def __init__(self, descr, docString, default, handleType, allowedType)
Definition: PropertyProxy.py:181
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.__get__
def __get__(self, obj, type=None)
Definition: PropertyProxy.py:198
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.__set__
def __set__(self, obj, value)
Definition: PropertyProxy.py:214
gaudirun.type
type
Definition: gaudirun.py:154
GaudiKernel.PropertyProxy.PropertyProxy.__init__
def __init__(self, descr, docString=None, default=None)
Definition: PropertyProxy.py:81
GaudiKernel.PropertyProxy.PropertyProxy.history
history
Definition: PropertyProxy.py:82
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase._confTypeName
_confTypeName
Definition: PropertyProxy.py:195
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.getDefaultConfigurable
def getDefaultConfigurable(self, typeAndName, requester)
Definition: PropertyProxy.py:232
GaudiKernel.PropertyProxy.GaudiHandleArrayPropertyProxy.arrayType
arrayType
Definition: PropertyProxy.py:360
GaudiKernel.PropertyProxy.PropertyProxy.__set__
def __set__(self, obj, value)
Definition: PropertyProxy.py:119
GaudiKernel.PropertyProxy.GaudiHandleArrayPropertyProxy.checkType
def checkType(self, obj, value)
Definition: PropertyProxy.py:362
GaudiKernel.PropertyProxy.DataHandlePropertyProxy.convertValueToBeSet
def convertValueToBeSet(self, obj, value)
Definition: PropertyProxy.py:419
GaudiKernel.GaudiHandles
Definition: GaudiHandles.py:1
GaudiKernel.PropertyProxy.GaudiHandleArrayPropertyProxy.convertDefaultToBeSet
def convertDefaultToBeSet(self, obj, default)
Definition: PropertyProxy.py:369
GaudiKernel.PropertyProxy.PropertyProxy.default
default
Definition: PropertyProxy.py:98
GaudiKernel.PropertyProxy.PropertyProxy.__delete__
def __delete__(self, obj)
Definition: PropertyProxy.py:172
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase._handleType
_handleType
Definition: PropertyProxy.py:194