Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v36r16 (ea80daf8)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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]:
61  if type(value) is tp:
62  # We need to check that the types match for lists, tuples and
63  # dictionaries (bug #34769).
64  return value
65  else:
66  raise ValueError(errmsg)
67  elif derives_from(tp, "Configurable"):
68  return value
69  else:
70  # all other types: accept if conversion allowed
71  try:
72  dummy = tp(value)
73  except (TypeError, ValueError):
74  raise ValueError(errmsg)
75 
76  return dummy # in case of e.g. classes with __int__, __iter__, etc. implemented
77 
78 
79 class PropertyProxy(object):
80  def __init__(self, descr, docString=None, default=None):
81  self.history = {}
82  self.descr = descr
83  self.deprecated = False
84  if docString:
85  self.__doc__ = docString
86  if "[[deprecated]]" in docString:
87  self.deprecated = True
88  if default is not None:
89  self.default = default
90 
91  def setDefault(self, value):
92  self.__default = value
93 
94  def getDefault(self):
95  return self.__default
96 
97  default = property(getDefault, setDefault)
98 
99  def fullPropertyName(self, obj):
100  return (obj.getJobOptName() or obj.getName()) + "." + self.descr.__name__
101 
102  def __get__(self, obj, type=None):
103  try:
104  return self.descr.__get__(obj, type)
105  except AttributeError:
106  # special case for lists and dictionaries:
107  # allow default to work with on += and []
108  if self.__default.__class__ in [list, dict]:
109  self.descr.__set__(obj, self.__default.__class__(self.__default))
110  return self.descr.__get__(obj, type)
111  else:
112  # for non lists (or dicts) return a reference to the default
113  # return self.__default
114  raise
115 
116  def __set__(self, obj, value):
117  # check if deprecated
118  if self.deprecated and not obj._unpickling:
119  log.warning(
120  "Property %s is deprecated: %s",
121  self.fullPropertyName(obj),
122  self.__doc__,
123  )
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 (
139  proptype
140  and not isinstance(None, proptype)
141  and not derives_from(value, "PropertyReference")
142  ):
143  try:
144  # check value itself
145  value = _isCompatible(proptype, value)
146 
147  # check element in case of list
148  if proptype == list:
149  try:
150  oldvec = self.descr.__get__(obj, type)
151  if oldvec:
152  tpo = type(oldvec[0])
153  for v in value:
154  _isCompatible(tpo, v)
155  except AttributeError:
156  # value not yet set
157  pass
158  except ValueError as e:
159  if allowcompat:
160  log.error(
161  "inconsistent value types for %s.%s (%s)"
162  % (obj.getName(), self.descr.__name__, str(e))
163  )
164  else:
165  raise
166 
167  # allow a property to be set if we're in non-default mode, or if it
168  # simply hasn't been set before
169  if not obj._isInSetDefaults() or obj not in self.history:
170  # by convention, 'None' for default is used to designate objects setting
171  if hasattr(self, "default") and self.default is None:
172  obj.__iadd__(value, self.descr) # to establish hierarchy
173  else:
174  self.descr.__set__(obj, value)
175  self.history.setdefault(obj, []).append(value)
176 
177  def __delete__(self, obj):
178  if obj in self.history:
179  del self.history[obj]
180  self.descr.__delete__(obj)
181 
182 
184  """A class with some utilities for GaudiHandles and GaudiHandleArrays"""
185 
186  def __init__(self, descr, docString, default, handleType, allowedType):
187  """<descr>: the real property in the object instance (from __slots__)
188  <docString>: the documentation string of this property
189  <default>: default value from C++ (via python generated by genconf)
190  <handleType>: real python handle type (e.g. PublicToolHandle, PrivateToolHandle, ...)
191  <allowedType>: allowed instance type for default
192  """
193  # check that default is of allowed type for this proxy
194  if not isinstance(default, allowedType):
195  raise TypeError(
196  "%s: %s default: %r is not a %s"
197  % (
198  descr.__name__,
199  self.__class__.__name__,
200  default,
201  allowedType.__name__,
202  )
203  )
204  PropertyProxy.__init__(self, descr, docString, default)
205  self._handleType = handleType
206  self._confTypeName = "Configurable" + handleType.componentType
207 
208  # print "%s: %r (%s)" % (self.__class__.__name__,self._handleType,self._confTypeName)
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:
219  self.__set__(obj, default)
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 = type(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 = type(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 type(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:102
GaudiKernel.PropertyProxy.PropertyProxy.__default
__default
Definition: PropertyProxy.py:92
GaudiKernel.PropertyProxy.PropertyProxy.setDefault
def setDefault(self, value)
Definition: PropertyProxy.py:91
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:94
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:44
GaudiKernel.DataHandle
Definition: DataHandle.py:1
GaudiKernel.PropertyProxy.PropertyProxy.deprecated
deprecated
Definition: PropertyProxy.py:83
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:29
GaudiKernel.PropertyProxy.PropertyProxy.__doc__
__doc__
Definition: PropertyProxy.py:85
GaudiKernel.PropertyProxy.PropertyProxyFactory
def PropertyProxyFactory(descr, doc, default)
Definition: PropertyProxy.py:469
GaudiKernel.PropertyProxy.PropertyProxy.descr
descr
Definition: PropertyProxy.py:82
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:99
GaudiKernel.PropertyProxy.DataHandlePropertyProxy.__init__
def __init__(self, descr, docString, default)
Definition: PropertyProxy.py:425
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase
Definition: PropertyProxy.py:183
GaudiKernel.PropertyProxy.PropertyProxy
Definition: PropertyProxy.py:79
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase.__init__
def __init__(self, descr, docString, default, handleType, allowedType)
Definition: PropertyProxy.py:186
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:226
gaudirun.type
type
Definition: gaudirun.py:162
GaudiKernel.PropertyProxy.PropertyProxy.__init__
def __init__(self, descr, docString=None, default=None)
Definition: PropertyProxy.py:80
GaudiKernel.PropertyProxy.PropertyProxy.history
history
Definition: PropertyProxy.py:81
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase._confTypeName
_confTypeName
Definition: PropertyProxy.py:206
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:116
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:491
GaudiKernel.PropertyProxy.GaudiHandleArrayPropertyProxy.convertDefaultToBeSet
def convertDefaultToBeSet(self, obj, default)
Definition: PropertyProxy.py:403
GaudiKernel.PropertyProxy.PropertyProxy.default
default
Definition: PropertyProxy.py:97
GaudiKernel.PropertyProxy.PropertyProxy.__delete__
def __delete__(self, obj)
Definition: PropertyProxy.py:177
GaudiKernel.PropertyProxy.GaudiHandlePropertyProxyBase._handleType
_handleType
Definition: PropertyProxy.py:205