| The Gaudi Framework v32r1 (f65d50dc)

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