00001
00002
00003
00004
00005
00006 __all__ = [ 'PropertyProxy', 'GaudiHandlePropertyProxy', 'GaudiHandleArrayPropertyProxy' ]
00007
00008 import os,glob
00009 from GaudiKernel.GaudiHandles import *
00010 from GaudiKernel import ConfigurableDb
00011
00012 import logging
00013 log = logging.getLogger( 'PropertyProxy' )
00014
00015
00016
00017
00018
00019 def derives_from(derived,base):
00020 """A string version of isinstance().
00021 <derived> is either an object instance, or a type
00022 <base> is a string containing the name of the base class (or <derived> class)"""
00023 if not isinstance(derived,type): derived=type(derived)
00024 if derived.__name__ == base: return True
00025 for b in derived.__bases__:
00026 if derives_from(b,base): return True
00027
00028 return False
00029
00030 def _isCompatible( tp, value ):
00031 errmsg = "received an instance of %s, but %s expected" % (type(value),tp)
00032
00033 if derives_from(value, 'PropertyReference'):
00034
00035 return value
00036 if ( tp is str ):
00037 if ( type(value) is str ) or derives_from(value, 'Configurable'):
00038
00039 return value
00040 else:
00041 raise ValueError( errmsg )
00042 elif ( tp in [ list, tuple, dict ] ):
00043 if ( type(value) is tp ):
00044
00045
00046 return value
00047 else:
00048 raise ValueError( errmsg )
00049 elif derives_from(tp, 'Configurable'):
00050 return value
00051 else:
00052
00053 try:
00054 dummy = tp( value )
00055 except (TypeError,ValueError):
00056 raise ValueError( errmsg )
00057
00058 return dummy
00059
00060
00061 class PropertyProxy( object ):
00062 def __init__( self, descr, docString=None, default=None ):
00063 self.history = {}
00064 self.descr = descr
00065 if docString:
00066 self.__doc__ = docString
00067 if default is not None:
00068 self.default = default
00069
00070
00071 def setDefault( self, value ):
00072 self.__default = value
00073
00074 def getDefault( self ):
00075 return self.__default
00076
00077 default = property( getDefault, setDefault )
00078
00079 def fullPropertyName( self, obj ):
00080 return (obj.getJobOptName() or obj.getName()) + '.' + self.descr.__name__
00081
00082 def __get__( self, obj, type = None ):
00083 try:
00084 return self.descr.__get__( obj, type )
00085 except AttributeError:
00086
00087
00088 if self.__default.__class__ in [ list, dict ]:
00089 self.descr.__set__( obj, self.__default.__class__(self.__default) )
00090 return self.descr.__get__( obj, type )
00091 else:
00092
00093
00094 raise
00095
00096 def __set__( self, obj, value ):
00097
00098 proptype, allowcompat = None, False
00099 if hasattr( self, 'default' ):
00100 proptype = type(self.default)
00101 if self.descr.__name__ == 'OutputLevel':
00102 allowcompat = True
00103 elif obj in self.history:
00104 proptype = type( self.history[ obj ][ 0 ] )
00105 allowcompat = True
00106
00107
00108
00109
00110 if proptype and proptype != type(None) and \
00111 not derives_from(value, 'PropertyReference'):
00112 try:
00113
00114 value = _isCompatible( proptype, value )
00115
00116
00117 if proptype == list:
00118 try:
00119 oldvec = self.descr.__get__( obj, type )
00120 if oldvec:
00121 tpo = type(oldvec[0])
00122 for v in value:
00123 _isCompatible( tpo, v )
00124 except AttributeError:
00125
00126 pass
00127 except ValueError, e:
00128 if allowcompat:
00129 log.error( 'inconsistent value types for %s.%s (%s)' %\
00130 (obj.getName(),self.descr.__name__,str(e)) )
00131 else:
00132 raise
00133
00134
00135
00136 if not obj._isInSetDefaults() or not obj in self.history:
00137
00138 if hasattr( self, 'default' ) and self.default == None:
00139 obj.__iadd__( value, self.descr )
00140 else:
00141 self.descr.__set__( obj, value )
00142 self.history.setdefault( obj, [] ).append( value )
00143
00144 def __delete__( self, obj ):
00145 if obj in self.history:
00146 del self.history[ obj ]
00147 self.descr.__delete__( obj )
00148
00149
00150
00151 class GaudiHandlePropertyProxyBase(PropertyProxy):
00152 """A class with some utilities for GaudiHandles and GaudiHandleArrays"""
00153
00154 def __init__(self, descr, docString, default, handleType, allowedType ):
00155 """<descr>: the real property in the object instance (from __slots__)
00156 <docString>: the documentation string of this property
00157 <default>: default value from C++ (via python generated by genconf)
00158 <handleType>: real python handle type (e.g. PublicToolHandle, PrivateToolHandle, ...)
00159 <allowedType>: allowed instance type for default
00160 """
00161
00162 if not isinstance(default,allowedType):
00163 raise TypeError( "%s: %s default: %r is not a %s" % \
00164 ( descr.__name__, self.__class__.__name__, default, allowedType.__name__ ) )
00165 PropertyProxy.__init__( self, descr, docString, default )
00166 self._handleType = handleType
00167 self._confTypeName = 'Configurable' + handleType.componentType
00168
00169
00170
00171 def __get__( self, obj, type = None ):
00172 try:
00173 return self.descr.__get__( obj, type )
00174 except AttributeError:
00175
00176 try:
00177 default = obj.__class__.getDefaultProperty( self.descr.__name__ )
00178 default = self.convertDefaultToBeSet( obj, default )
00179 if default:
00180 self.__set__( obj, default )
00181 except AttributeError,e:
00182
00183 raise RuntimeError(*e.args)
00184
00185 return self.descr.__get__( obj, type )
00186
00187
00188 def __set__( self, obj, value ):
00189
00190
00191 if not obj._isInSetDefaults() or not obj in self.history:
00192 value = self.convertValueToBeSet( obj, value )
00193
00194 self.descr.__set__( obj, value )
00195 log.debug( "Setting %s = %r", self.fullPropertyName( obj ), value )
00196 self.history.setdefault( obj, [] ).append( value )
00197
00198
00199
00200 def isHandle(self,value):
00201 """Check if <value> is a handle of the correct type"""
00202 return isinstance(value,self._handleType)
00203
00204
00205 def isConfig(self,value):
00206 """Check if <value> is a configurable of the correct type"""
00207 return derives_from(value,self._confTypeName)
00208
00209
00210 def getDefaultConfigurable(self,typeAndName,requester):
00211 """Return the configurable instance corresponding to the toolhandle if possible.
00212 Otherwise return None"""
00213 global log
00214
00215 typeAndNameTuple = typeAndName.split('/')
00216 confType = typeAndNameTuple[0]
00217 confClass=ConfigurableDb.getConfigurable(confType)
00218
00219 if not derives_from(confClass,self._confTypeName):
00220 log.error( "%s: Configurable %s is not a %s",
00221 requester, confType, self._confTypeName )
00222 return None
00223 try:
00224 confName = typeAndNameTuple[1]
00225 except IndexError:
00226 return confClass()
00227 else:
00228 return confClass(confName)
00229
00230
00231 def convertDefaultToBeSet( self, obj, default ):
00232
00233 isString = type(default) == str
00234 if not isString and self.isConfig(default):
00235
00236 return default
00237 elif isString or self.isHandle(default):
00238 if isString:
00239
00240 typeAndName = default
00241 default = self._handleType( typeAndName )
00242 else:
00243 typeAndName = default.typeAndName
00244 if not self._handleType.isPublic:
00245 if not typeAndName:
00246 return None
00247
00248 try:
00249 conf = self.getDefaultConfigurable(typeAndName, self.fullPropertyName(obj))
00250
00251 except AttributeError,e:
00252
00253 raise RuntimeError(*e.args)
00254 if conf is None:
00255 raise RuntimeError( "%s: Default configurable for class %s not found in ConfigurableDb.CfgDb" % \
00256 (self.fullPropertyName(obj),default.getType() ) )
00257 return conf
00258 else:
00259 raise TypeError( "%s: default value %r is not of type %s or %s" % \
00260 (self.fullPropertyName(obj),default,self._confTypeName,self._handleType.__name__) )
00261
00262 return default
00263
00264 def convertValueToBeSet( self, obj, value ):
00265 if value is None: value = ''
00266 isString = type(value) == str
00267 if isString:
00268
00269 return self._handleType(value)
00270 elif self.isHandle(value):
00271
00272 return self._handleType(value.toStringProperty())
00273 elif self.isConfig(value):
00274 if self._handleType.isPublic:
00275
00276 if derives_from(value,'ConfigurableAlgTool'):
00277 if not value.isInToolSvc():
00278 suggestion = 'You may need to add jobOptions lines something like:' + os.linesep + \
00279 'from AthenaCommon.AppMgr import ToolSvc' + os.linesep + \
00280 'ToolSvc += '
00281 if value.getName() == value.getType():
00282 suggestion += '%s()' % value.__class__.__name__
00283 else:
00284 suggestion += '%s(%r)' % (value.__class__.__name__,value.getName())
00285 raise RuntimeError( self.fullPropertyName(obj) +
00286 ': Public tool %s is not yet in ToolSvc. %s' %
00287 (value.getJobOptName(),suggestion) )
00288
00289 return self._handleType(value.toStringProperty())
00290 elif value.hasParent( obj.getJobOptName() ):
00291
00292 return value
00293 else:
00294
00295 value = obj.copyChildAndSetParent( value, obj.getJobOptName() )
00296
00297 obj.allConfigurables[value.name()] = value
00298 return value
00299 else:
00300 raise TypeError( "Property %s value %r is not a %s nor a %s nor a string" % \
00301 (self.fullPropertyName(obj),value,self._confTypeName,self._handleType.__name__) )
00302
00303 return value
00304
00305
00306 class GaudiHandlePropertyProxy(GaudiHandlePropertyProxyBase):
00307 def __init__( self, descr, docString, default ):
00308 GaudiHandlePropertyProxyBase.__init__( self, descr, docString, default, type(default), GaudiHandle )
00309
00310
00311 class GaudiHandleArrayPropertyProxy(GaudiHandlePropertyProxyBase):
00312 def __init__( self, descr, docString, default ):
00313 """<descr>: the real property in the object instance (from __slots__)
00314 <confTypeName>: string indicating the (base) class of allowed Configurables to be assigned.
00315 <handleType>: real python handle type (e.g. PublicToolHandle, PrivateToolHandle, ...)
00316 """
00317 GaudiHandlePropertyProxyBase.__init__( self, descr, docString, default, type(default).handleType, GaudiHandleArray )
00318 self.arrayType = type(default)
00319
00320
00321 def checkType( self, obj, value ):
00322 if not isinstance( value, list ) and not isinstance( value, self.arrayType ):
00323 raise TypeError( "%s: Value %r is not a list nor a %s" % \
00324 ( self.fullPropertyName(obj), value, self.arrayType.__name__ ) )
00325
00326
00327 def convertDefaultToBeSet( self, obj, default ):
00328 self.checkType( obj, default )
00329 newDefault = self.arrayType()
00330 for d in default:
00331 cd = GaudiHandlePropertyProxyBase.convertDefaultToBeSet( self, obj, d )
00332 if cd: newDefault.append( cd )
00333
00334 return newDefault
00335
00336
00337 def convertValueToBeSet( self, obj, value ):
00338 self.checkType( obj, value )
00339 newValue = self.arrayType()
00340 for v in value:
00341 cv = GaudiHandlePropertyProxyBase.convertValueToBeSet( self, obj, v )
00342 if cv: newValue.append( cv )
00343
00344 return newValue
00345
00346
00347
00348 def PropertyProxyFactory( descr, doc, default ):
00349
00350 if isinstance(default,GaudiHandleArray):
00351 return GaudiHandleArrayPropertyProxy( descr, doc, default )
00352
00353 if isinstance(default,GaudiHandle):
00354 return GaudiHandlePropertyProxy( descr, doc, default )
00355
00356 return PropertyProxy( descr, doc, default )