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