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