| # Gaudi Framework, version v21r8 |

Home |  | Generated: 17 Mar 2010

# Configurable.py

Go to the documentation of this file.

    00001 # File: AthenaCommon/python/Configurable.py
    00002 # Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
    00003 # Author: Martin Woudstra (Martin.Woudstra@cern.ch)
    00004
    00005 import copy, string, types, os
    00006 from inspect import isclass
    00007 import GaudiKernel.ConfigurableMeta as ConfigurableMeta
    00008 from GaudiKernel.Constants import error_explanation, \
    00009                                   VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL
    00010 from GaudiKernel.PropertyProxy import PropertyProxy
    00011 from GaudiKernel.GaudiHandles import *
    00012
    00013 ### data ---------------------------------------------------------------------
    00014 __all__ = [ 'Configurable',
    00015             'ConfigurableAlgorithm',
    00016             'ConfigurableAlgTool',
    00017             'ConfigurableAuditor',
    00018             'ConfigurableService',
    00019             'ConfigurableUser',
    00020             'VERBOSE','DEBUG','INFO', 'WARNING', 'ERROR', 'FATAL',
    00021             'appendPostConfigAction', 'removePostConfigAction' ]
    00022
    00023 ## for messaging
    00024 import logging
    00025 log = logging.getLogger( 'Configurable' )
    00026
    00027 def expandvars(data):
    00028     """
    00029     Expand environment variables "data".
    00030     Data can be string, list, tuple and dictionary. For collection, all the
    00031     contained strings will be manipulated (recursively).
    00032     """
    00033     import os.path
    00034     typ = type(data)
    00035     if typ is str:
    00036         return os.path.expandvars(data)
    00037     elif typ in [list, tuple]:
    00038         collect = []
    00039         for i in data:
    00040             collect.append(expandvars(i))
    00041         return typ(collect)
    00042     elif typ is dict:
    00043         collect = {}
    00044         for k in data:
    00045             collect[expandvars(k)] = expandvars(data[k])
    00046         return collect
    00047     return data
    00048
    00049 class Error(RuntimeError):
    00050     """
    00051     Error occurred in the configuration process.
    00052     """
    00053     pass
    00054
    00055 ## Allow references to options  as in old style
    00056 class PropertyReference(object):
    00057     def __init__(self,propname):
    00058         self.name = propname
    00059     def __repr__(self):
    00060         return "@%s"%self.name
    00061     def __resolve__(self):
    00062         # late binding for property references
    00063         retval = None
    00064         refname, refprop = self.name.rsplit('.',1)
    00065         if refname in Configurable.allConfigurables:
    00066             conf = Configurable.allConfigurables[refname]
    00067             retval = getattr(conf,refprop)
    00068             if hasattr(retval,"getFullName"):
    00069                 retval = retval.getFullName()
    00070         else:
    00071             raise NameError("name '%s' not found resolving '%s'"%(refname,self))
    00072         return retval
    00073     def getFullName(self):
    00074         """This function allow transparent integration with
    00075         Configurable.getValuedProperties.
    00076         """
    00077         try:
    00078             return self.__resolve__()
    00079         except NameError:
    00080             # ignore the error if we cannot resolve the name yet
    00081             return self
    00082         except AttributeError:
    00083             # ignore the error if we cannot resolve the attribute yet
    00084             return self
    00085
    00086 ### base class for configurable Gaudi algorithms/services/algtools/etc. ======
    00087 class Configurable( object ):
    00088     """Base class for Gaudi components that implement the IProperty interface.
    00089        Provides most of the boilerplate code, but the actual useful classes
    00090        are its derived ConfigurableAlgorithm, ConfigurableService, and
    00091        ConfigurableAlgTool."""
    00092
    00093     ## for detecting the default name
    00094     class DefaultName:
    00095         pass
    00096
    00097     propertyNoValue = '<no value>'
    00098     indentUnit = '| '
    00099     printHeaderWidth=100
    00100     printHeaderPre=5
    00101
    00102     __metaclass__ = ConfigurableMeta.ConfigurableMeta
    00103
    00104     __slots__ = (
    00105        '__children',           # controlled components, e.g. private AlgTools
    00106        '__tools',              # private AlgTools  (#PM-->)
    00107        '_name',                # the (unqualified) component name
    00108        '_inSetDefaults',       # currently setting default values
    00109        '_initok',              # used to enforce base class init
    00110        '_setupok'              # for debugging purposes (temporary)
    00111     )
    00112
    00113     allConfigurables = {}      # just names would do, but currently refs to the actual
    00114     configurableServices = {}  # just names would do, but currently refs to the actual
    00115                                # configurables is needed  for (temporary) backwards
    00116                                # compatibility; will change in the future
    00117     _configurationLocked = False
    00118
    00119     def __new__ ( cls, *args, **kwargs ):
    00120         """To Gaudi, any object with the same type/name is the same object. Hence,
    00121            this is mimicked in the configuration: instantiating a new Configurable
    00122            of a type with the same name will return the same instance."""
    00123
    00124         global log
    00125         # try to get the name of the Configurable (having a name is compulsory)
    00126         if 'name' in kwargs:
    00127         # simple keyword (by far the easiest)
    00128             name = kwargs[ 'name' ]
    00129         elif 'name' in cls.__init__.func_code.co_varnames:
    00130         # either positional in args, or default
    00131             index =  list(cls.__init__.func_code.co_varnames).index( 'name' )
    00132             try:
    00133              # var names index is offset by one as __init__ is to be called with self
    00134                 name = args[ index - 1 ]
    00135             except IndexError:
    00136              # retrieve default value, then
    00137                 name = cls.__init__.func_defaults[ index - (len(args)+1) ]
    00138         else:
    00139         # positional index is assumed (will work most of the time)
    00140             try:
    00141                 name = args[1]    # '0' is for self
    00142             except (IndexError,TypeError):
    00143                 raise TypeError( 'no "name" argument while instantiating "%s"' % cls.__name__ )
    00144
    00145         argname = name
    00146         if name == Configurable.DefaultName :
    00147             if hasattr(cls, 'DefaultedName' ) :
    00148                 name = cls.DefaultedName
    00149             else :
    00150                 name = cls.getType()
    00151         elif not name or type(name) != str:
    00152         # unnamed, highly specialized user code, etc. ... unacceptable
    00153             raise TypeError( 'could not retrieve name from %s.__init__ arguments' % cls.__name__ )
    00154
    00155         # Handle the case of global tools to prepend ToolSvc in the name.
    00156         # This is needed for compatibility with old JobOptions files being read
    00157         if issubclass( cls, ConfigurableAlgTool) and '.' not in name :
    00158             name = 'ToolSvc.' + name
    00159
    00160         # close backdoor access to otherwise private subalgs/tools
    00161         #PM if 0 <= name.find( '.' ):
    00162         #PM # temp protection for old style types
    00163         #PM   from OldStyleConfig import GenericConfigurable
    00164         #PM   if not issubclass( cls, GenericConfigurable ): # except raised for new types only
    00165         #PM      raise NameError( '"%s": backdoor access to private configurables not allowed' % name )
    00166
    00167         # ordinary recycle case
    00168         if name in cls.configurables:
    00169             conf = cls.configurables[ name ]
    00170             if name != argname:      # special case: user derived <-> real ... make same
    00171                 cls.configurables[ conf.getType() ] = conf
    00172             #---PM: Initialize additional properties
    00173             for n,v in kwargs.items():
    00174                 if n != "name": # it should not be confused with a normal property
    00175                     setattr(conf, n, v)
    00176             if not cls._configurationLocked and not "_enabled" in kwargs and isinstance(conf, ConfigurableUser):
    00177                 # Ensure that the ConfigurableUser gets enabled if nothing is
    00178                 # specified in the constructor.
    00179                 setattr(conf, "_enabled", True)
    00180             return conf
    00181
    00182         # a couple of special cases (note that these cases don't mix)
    00183         spos = name.find( '/' )
    00184         ti_name = None
    00185         if spos < 0:
    00186             ti_name = "%s/%s" % (name,name)
    00187             if ti_name in cls.configurables:
    00188                 # support for old-style name as type/name lookup where name==type
    00189                 return cls.configurables[ ti_name ]
    00190
    00191         i_name = None
    00192         if spos > 0:
    00193             i_name = name[:spos]
    00194             if i_name == name[spos+1:] and i_name in cls.configurables:
    00195             # this is the opposite of the above special case
    00196                 return cls.configurables[ i_name ]
    00197
    00198         # the following is purely for debugging support and should realistically bomb
    00199         conf = cls.allConfigurables.get( name, None ) or\
    00200                  (spos < 0 and cls.allConfigurables.get( ti_name, None )) or\
    00201                  (spos > 0 and i_name == name[spos+1:] and cls.allConfigurables.get( i_name, None ))
    00202         if conf:                    # wrong type used?
    00203             if conf.__class__ is ConfigurableGeneric :
    00204                 #  If the instance found is ConfigurableGeneric then
    00205                 #  we create a new one with the proper type and fill with
    00206                 #  the contents of the generic one
    00207                 newconf = object.__new__( cls, *args, **kwargs )
    00208                 cls.__init__( newconf, *args )
    00209                 #  initialize with the properties of generic configurable
    00210                 #  (we map the names of the properties to lowercase versions because
    00211                 #  old options are not case sensitive)
    00212                 names = {}
    00213                 for n in newconf.__slots__:
    00214                     names[n.lower()] = n
    00215                 for n in conf._properties:
    00216                     if names[n.lower()] != n:
    00217                         log.warning( "Option '%s' was used for %s, but the correct spelling is '%s'"%(n,name,names[n.lower()]) )
    00218                     setattr(newconf, names[n.lower()], getattr( conf, n ) )
    00219                 for n,v in kwargs.items():
    00220                     setattr(newconf, n, v)
    00221                 cls.configurables[ name ] = newconf
    00222                 cls.allConfigurables[ name ] = newconf
    00223                 return newconf
    00224             else :
    00225                 #  will be an actual error in the future (now only report as such)
    00226                 log.error( 'attempt to redefine type of "%s" (was: %s, new: %s)%s',
    00227                 name, conf.__class__.__name__, cls.__name__, error_explanation )
    00228                 #  in the future:
    00229                 #  return None             # will bomb on use (or go unharmed on non-use)
    00230                 #  for now, allow use through allConfigurables lookup
    00231                 #---PM: Initialize additional properties
    00232                 for n,v in kwargs.items():
    00233                     setattr(conf, n, v)
    00234                 return conf
    00235
    00236         # still here: create a new instance and initialize it
    00237         conf = object.__new__( cls, *args, **kwargs )
    00238         cls.__init__( conf, *args, **kwargs )
    00239
    00240         # update normal, per-class cache
    00241         cls.configurables[ name ] = conf
    00242
    00243         for base in cls.__bases__:
    00244             if base.__name__ == 'ConfigurableService':
    00245                 cls.configurableServices[ name ] = conf
    00246
    00247         # update generics super-cache, if needed
    00248         cls.allConfigurables[ name ] = conf
    00249         #-->PM#if hasattr( cls, 'getType' ) and name.find('/') < 0:
    00250         #-->PM#   cls.allConfigurables[ cls.getType() + '/' + name ] = conf
    00251
    00252         return conf
    00253
    00254     def __init__( self, name = DefaultName ):
    00255         # check class readiness, all required overloads should be there now
    00256         klass = self.__class__
    00257
    00258         # this is an abstract class
    00259         if klass == Configurable:
    00260             raise TypeError, "%s is an ABC and can not be instantiated" % str(Configurable)
    00261
    00262         # the following methods require overloading
    00263         #NOT YET  meths = { 'getServices'   : 1,    # retrieve list of services to configure
    00264         meths = { 'getDlls'       : 1,    # provide list of Dlls to load
    00265                   'getGaudiType'  : 1,    # return string describing component class
    00266                   'getHandle'     : 1 }   # provide access to C++ side component instance
    00267 #                'getType'       : 1 }   # return the type of the actual C++ component
    00268
    00269         for meth, nArgs in meths.items():
    00270             try:
    00271                 f = getattr( klass, meth ).im_func
    00272             except AttributeError:
    00273                 raise NotImplementedError, "%s is missing in class %s" % (meth,str(klass))
    00274
    00275             # in addition, verify the number of arguments w/o defaults
    00276             nargcount = f.func_code.co_argcount
    00277             ndefaults = f.func_defaults and len(f.func_defaults) or 0
    00278             if not nargcount - ndefaults <= nArgs <= nargcount:
    00279                 raise TypeError, "%s.%s requires exactly %d arguments" % (klass,meth,nArgs)
    00280
    00281         # for using this Configurable as a (Gaudi) sequence
    00282         self.__children = []
    00283         self.__tools = {}
    00284
    00285         # know who we are
    00286         if name == Configurable.DefaultName :
    00287             if hasattr(self.__class__, 'DefaultedName' ) :
    00288                 self._name = self.__class__.DefaultedName
    00289             else :
    00290                 self._name = self.getType()
    00291         else :
    00292             self._name = name
    00293
    00294         # set to True when collecting defaults, False otherwise
    00295         self._inSetDefaults = False
    00296
    00297         # for later, in case __init__ itself is overridden
    00298         self._initok = True
    00299
    00300         # for debugging purposes (temporary)
    00301         self._setupok = False
    00302
    00303     # pickle support
    00304     def __getstate__ (self):
    00305         dict = {}
    00306         for name, proxy in self._properties.items():
    00307             try:
    00308                 dict[ name ] = proxy.__get__( self )
    00309             except AttributeError:
    00310                 pass
    00311
    00312         dict[ '_Configurable__children' ] = self.__children
    00313         dict[ '_Configurable__tools' ] = self.__tools
    00314         dict[ '_name' ] = self._name
    00315         return dict
    00316
    00317     def __getnewargs__(self) :
    00318         return (self._name,)
    00319
    00320     def __setstate__ ( self, dict ):
    00321         self._initok = True
    00322         for n, v in dict.items():
    00323             setattr (self, n, v)
    00324         return
    00325
    00326     # to allow a few basic sanity checks, as well as nice syntax
    00327     def __len__( self ):
    00328         return len( self.__children )
    00329
    00330     def __iter__( self ):
    00331         return iter( self.__children )
    00332
    00333     # ownership rules of self through copying
    00334     def __deepcopy__( self, memo ):
    00335         newconf = object.__new__( self.__class__, self.getName() )
    00336         self.__class__.__init__( newconf, self.getName() )
    00337
    00338         for proxy in self._properties.values():
    00339             try:
    00340                 proxy.__set__( newconf, proxy.__get__( self ) )
    00341             except AttributeError:
    00342                 pass                   # means property was not set for self
    00343
    00344         for c in self.__children:
    00345             newconf += c              # processes proper copy semantics
    00346
    00347         return newconf
    00348
    00349     # hierarchy building, and ownership rules of children
    00350     def __iadd__( self, configs, descr = None ):
    00351         if not type(configs) in (list,tuple):
    00352             configs = ( configs, )
    00353
    00354         joname = self.getJobOptName()
    00355
    00356         for cfg in configs:
    00357             # prevent type mismatches
    00358             if not isinstance( cfg, Configurable ):
    00359                 raise TypeError( "'%s' is not a Configurable" % str(cfg) )
    00360
    00361             cc = self.copyChildAndSetParent( cfg, joname )
    00362
    00363             # filters dupes; usually "ok" (backdoor should catch them)
    00364             ccjo = cc.getJobOptName()
    00365             for c in self.__children:
    00366                 if c.getJobOptName() == ccjo:
    00367                     log.error( 'attempt to add a duplicate ... dupe ignored%s', error_explanation )
    00368                     break
    00369             else:
    00370                 self.__children.append( cc )
    00371
    00372             try:
    00373                 if descr:         # support for tool properties
    00374                     descr.__set__( self, cc )
    00375                 else:
    00376                     setattr( self, cc.getName(), cc )
    00377             except AttributeError:
    00378                 pass              # to allow free addition of tools/subalgorithms
    00379
    00380         return self
    00381
    00382     def __getattr__( self, attr ):  # until ToolProperties exist ...
    00383
    00384         if attr in self.__tools : return self.__tools[attr]
    00385
    00386         for c in self.__children:
    00387             if c.getName() == attr:
    00388                 return c
    00389
    00390         raise AttributeError( "'%s' object has no attribute '%s'" % (self.__class__,attr) )
    00391
    00392     def __setattr__( self, name, value ) :
    00393         if self._configurationLocked:
    00394             raise RuntimeError("%s: Configuration cannot be modified after the ApplicationMgr has been started."%self.name())
    00395         try :
    00396             super( Configurable, self ).__setattr__( name, value )
    00397         except AttributeError:
    00398             raise AttributeError( "Configurable '%s' does not have property '%s'."
    00399                                   % ( self.__class__.__name__, name) )
    00400
    00401     def __delattr__( self, attr ):
    00402         # remove as property, otherwise try as child
    00403         try:
    00404             # remove history etc., then reset to default (in case set before)
    00405             prop = self._properties[ attr ]
    00406             prop.__delete__( self )
    00407             prop.__set__( self, prop.default )
    00408             return               # reaches here? was property: done now
    00409         except KeyError:
    00410             pass
    00411         # otherwise, remove the private tool
    00412         if attr in self.__tools :
    00413             del self.__tools[attr]
    00414
    00415         # otherwise, remove child, if one is so named
    00416         for c in self.__children:
    00417             if c.getName() == attr:
    00418                 self.__children.remove( c )
    00419
    00420         # potentially, there are left over caches (certain user derived classes)
    00421         try:
    00422             del self.__dict__[ attr ]
    00423         except (AttributeError,KeyError):
    00424             pass
    00425
    00426     def __nonzero__(self):
    00427         return True
    00428
    00429
    00430     def remove( self, items ):
    00431         if type(items) != list and type(items) != tuple:
    00432             items = [ items ]
    00433
    00434         self.__children = [ e for e in self.__children if not e in items ]
    00435
    00436     def removeAll( self ):
    00437         self.remove( self.__children )
    00438
    00439     # called by __iadd__; determines child copy semantics
    00440     def copyChild( self, child ):
    00441         return copy.deepcopy( child )
    00442
    00443     def setParent( self, parentName ):
    00444         pass
    00445
    00446     def getParent( self ):
    00447         return ""
    00448
    00449     def hasParent( self, parent ):
    00450         return False
    00451
    00452     def copyChildAndSetParent(self,cfg,parent):
    00453         cc = self.copyChild( cfg )
    00454
    00455         if hasattr( cc, 'setParent' ) and parent:
    00456             try:
    00457                 cc.setParent( parent )
    00458             except RuntimeError, e:
    00459                 # temporary backdoor resolution for compatibility
    00460                 log.error( str(e) + '%s', error_explanation )
    00461                 ccbd = cc.configurables[ cc.getJobOptName() ]
    00462
    00463                 # merge properties, new over pre-existing
    00464                 for proxy in self._properties.values():
    00465                     if proxy.history.has_key( cc ):
    00466                         proxy.__set__( ccbd, proxy.__get__( cc ) )
    00467
    00468                 # consolidate
    00469                 cc = ccbd
    00470         return cc
    00471
    00472     def getChildren( self ):
    00473         return self.__children[:]    # read only
    00474
    00475     def getTools( self ):
    00476         return self.__tools.values()    # read only
    00477
    00478     def children( self ):
    00479         log.error( "children() is deprecated, use getChildren() instead for consistency" )
    00480         log.error( "getChildren() returns a copy; to add a child, use 'parent += child'%s",
    00481                    error_explanation )
    00482         return self.__children       # by ref, for compatibility
    00483
    00484     def getAllChildren( self ):
    00485         """Get all (private) configurable children, both explicit ones (added with +=)
    00486         and the ones in the private GaudiHandle properties"""
    00487         childs = []
    00488         # add private configurable properties (also inside handles)
    00489         for proxy in self._properties.values():
    00490             try:
    00491                 c = proxy.__get__( self )
    00492             except AttributeError:
    00493                 pass
    00494             else:
    00495                 if isinstance(c,Configurable) and not c.isPublic():
    00496                     childs.append(c)
    00497                 elif isinstance(c,GaudiHandle):
    00498                     try:
    00499                         conf = c.configurable
    00500                     except AttributeError:
    00501                         pass
    00502                     else:
    00503                         if not conf.isPublic():
    00504                             childs.append(conf)
    00505                 elif isinstance(c,GaudiHandleArray):
    00506                     # only setup private arrays
    00507                     if not c.isPublic():
    00508                         for ci in c:
    00509                             if isinstance(ci,Configurable):
    00510                                 childs.append(ci)
    00511                             else:
    00512                                 try:
    00513                                     conf = ci.configurable
    00514                                 except AttributeError:
    00515                                     pass
    00516                                 else:
    00517                                     childs.append(conf)
    00518
    00519         # add explicit children
    00520         childs += self.__children
    00521         return childs
    00522
    00523     def getSequence( self ):
    00524         elems = []
    00525         for c in self.__children:
    00526             elems.append( c.getFullName() )
    00527         return elems
    00528
    00529     def setup( self ):
    00530         # make sure base class init has been called
    00531         if not hasattr(self,'_initok') or not self._initok:
    00532         # could check more, but this is the only explanation
    00533             raise TypeError, \
    00534                "Configurable.__init__ not called in %s override" % self.__class__.__name__
    00535
    00536 #      log.debug("calling setup() on " + self.getFullJobOptName())
    00537
    00538         # setup self: this collects all values on the python side
    00539         self.__setupServices()
    00540         self.__setupDlls()
    00541         self.__setupDefaults()
    00542
    00543         # setup children
    00544         for c in self.getAllChildren():
    00545             c.setup()
    00546
    00547         # now get handle to work with for moving properties into the catalogue
    00548         handle = self.getHandle()
    00549         if not handle:
    00550             log.debug( 'no handle for %s: not transporting properties', self._name )
    00551             return                    # allowed, done early
    00552
    00553         # pass final set of properties on to handle on the C++ side or JobOptSvc
    00554         for name in self._properties.keys():
    00555             if hasattr( self, name ): # means property has python-side value/default
    00556                 setattr( handle, name, getattr(self,name) )
    00557
    00558         # for debugging purposes
    00559         self._setupok = True
    00560
    00561     def getProperties( self ):
    00562         props = {}
    00563         for name, proxy in self._properties.items():
    00564             try:
    00565                 props[ name ] = proxy.__get__( self )
    00566             except AttributeError:
    00567                 props[ name ] = Configurable.propertyNoValue
    00568
    00569         return props
    00570
    00571     def getValuedProperties( self ):
    00572         props = {}
    00573         for name, proxy in self._properties.items():
    00574             if self.isPropertySet(name):
    00575                 value = proxy.__get__( self )
    00576                 if hasattr(value, 'getFullName') :
    00577                     value = value.getFullName()
    00578                 elif type(value) in [list, tuple]:
    00579                     new_value = []
    00580                     for i in value:
    00581                         if hasattr(i, 'getFullName'):
    00582                             new_value.append(i.getFullName())
    00583                         else:
    00584                             new_value.append(i)
    00585                     value = type(value)(new_value)
    00586                 elif type(value) is dict:
    00587                     new_value = {}
    00588                     for i in value:
    00589                         if hasattr(value[i], 'getFullName'):
    00590                             new_value[i] = value[i].getFullName()
    00591                         else:
    00592                             new_value[i] = value[i]
    00593                     value = new_value
    00594                 props[ name ] = value
    00595
    00596         return props
    00597
    00598     def properties( self ):
    00599         return self.getProperties()           # compatibility
    00600
    00601     @classmethod
    00602     def getDefaultProperties( cls ):
    00603         class collector:
    00604             pass
    00605
    00606         # user provided defaults
    00607         c = collector()
    00608         cls.setDefaults( c )
    00609
    00610         # defaults from C++
    00611         for k,v in cls._properties.items():
    00612             if not k in c.__dict__ and hasattr( v, 'default' ):
    00613                 c.__dict__[ k ] = v.default
    00614
    00615         return c.__dict__
    00616
    00617     @classmethod
    00618     def getDefaultProperty( cls, name ):
    00619         class collector:
    00620             pass
    00621
    00622         # user provided defaults
    00623         c = collector()
    00624         cls.setDefaults( c )
    00625
    00626         if name in c.__dict__:
    00627             return c.__dict__[ name ]
    00628
    00629         # defaults from C++
    00630         try:
    00631             v = cls._properties[name]
    00632             if hasattr( v, 'default' ):
    00633                 return v.default
    00634         except KeyError:
    00635             pass
    00636
    00637         return None
    00638
    00639     def getProp(self, name):
    00640         """Returns the value of the given property.
    00641         """
    00642         if hasattr(self, name):
    00643             return getattr(self, name)
    00644         else:
    00645             return self.getDefaultProperties()[name]
    00646
    00647     def setProp(self, name, value):
    00648         """Set the value of a given property
    00649         """
    00650         return setattr(self, name, value)
    00651
    00652     def isPropertySet(self, name):
    00653         """Tell if the property 'name' has been set or not.
    00654
    00655         Because of a problem with list and dictionary properties, in those cases
    00656         if the value is equal to the default, the property is considered as not
    00657         set.
    00658         """
    00659         if not hasattr(self, name):
    00660             return False
    00661         else:
    00662             try:
    00663                 default = self.getDefaultProperties()[name]
    00664                 if isinstance(default, (list, dict)):
    00665                     value = getattr(self, name)
    00666                     return value != default
    00667             except KeyError:
    00668                 pass # no default found
    00669             return True
    00670
    00671     def getType( cls ):
    00672         return cls.__name__
    00673
    00674     def getName( self ):
    00675         return self._name
    00676
    00677     def name( self ):
    00678         return self.getName()
    00679
    00680     def getJobOptName( self ):               # full hierachical name
    00681         return self.getName()
    00682
    00683     def isPublic( self ):
    00684         return True
    00685
    00686     # for a couple of existing uses out there
    00687     def jobOptName( self ):
    00688         log.error( "jobOptName() is deprecated, use getJobOptName() instead for consistency%s",
    00689                    error_explanation )
    00690         return self.getJobOptName()           # compatibility
    00691
    00692     def getFullName( self ) :
    00693         return str( self.getType() + '/' + self.getName() )
    00694
    00695     def getFullJobOptName( self ):
    00696         return "%s/%s" % (self.getType(),self.getJobOptName() or self.getName())
    00697
    00698     def getPrintTitle(self):
    00699         return self.getGaudiType() + ' ' + self.getTitleName()
    00700
    00701     def getTitleName( self ):
    00702         if log.isEnabledFor( logging.DEBUG ):
    00703             return self.getFullJobOptName()
    00704         else:
    00705             return self.getFullName()
    00706
    00707     def setDefaults( cls, handle ):
    00708         pass
    00709
    00710     def clone( self, name = None, **kwargs  ) :
    00711         if not name :
    00712             if hasattr(self, 'DefaultedName' ) : name = self.DefaultedName
    00713             else                               : name = self.getType()
    00714
    00715         newconf = Configurable.__new__( self.__class__, name )
    00716         self.__class__.__init__( newconf, name )
    00717
    00718         for proxy in self._properties.values():
    00719             try :
    00720                 value = proxy.__get__( self )
    00721                 if type(value) in [ str, list, dict, tuple ]:
    00722                     # clone the values of the properties for basic types
    00723                     value = type(value)(value)
    00724                 proxy.__set__( newconf, value )
    00725             except AttributeError:
    00726                 pass
    00727
    00728         for c in self.__children:
    00729             newconf += c              # processes proper copy semantics
    00730
    00731         for n , t in self.__tools.items():
    00732             newconf.addTool(t, n)
    00733
    00734         for name, value in kwargs.items():
    00735             setattr(newconf, name, value)
    00736
    00737         return newconf
    00738
    00739     def splitName( self ) :
    00740         fullname = self.getName()
    00741         dot = fullname.find('.')
    00742         if dot != -1 :
    00743             parentname = fullname[:dot]
    00744             longname = fullname[dot+1:]
    00745         else :
    00746             parentname = ''
    00747             longname = fullname
    00748         dot = longname.find('.')
    00749         if dot != -1 :
    00750             name = longname[:dot]
    00751         else :
    00752             name = longname
    00753         return parentname, name, longname
    00754
    00755     def addTool( self, tool, name = None ) :
    00756         if isclass(tool) and issubclass(tool, ConfigurableAlgTool):
    00757             if name is None:
    00758                 name = tool.__name__
    00759             priv_tool = tool( self.getName()+ '.' + name )
    00760         elif isinstance(tool, ConfigurableAlgTool):
    00761             if name is None:
    00762                 name = tool.splitName()[1]
    00763             priv_tool = tool.clone( self.getName()+ '.' + name )
    00764         else:
    00765             if isclass(tool):
    00766                 classname = tool.__name__
    00767             else:
    00768                 classname = type(tool).__name__
    00769             raise TypeError, "addTool requires AlgTool configurable. Got %s type" % classname
    00770         self.__tools[name] =  priv_tool
    00771         if name in self.__slots__:
    00772             # this is to avoid that the property hides the tool
    00773             setattr(self,name,self.__tools[name])
    00774
    00775     def _isInSetDefaults( self ):
    00776         return self._inSetDefaults
    00777
    00778     def __setupServices( self ):
    00779         #svcs = self.getServices()
    00780         #if not svcs:
    00781         svcs = []
    00782         #elif type(svcs) == types.StringType:
    00783         #   svcs = [ svcs ]
    00784
    00785         import __main__
    00786         for svc in svcs:
    00787             handle = __main__.Service( svc )
    00788             # services should be configurables as well, but aren't for now
    00789             # handle.setup()
    00790
    00791             # allow Configurable to make some changes
    00792             if hasattr( self, 'configure' + svc ):
    00793                 eval( 'self.configure' + svc + '( handle )' )
    00794
    00795     def __setupDlls( self ):
    00796         dlls = self.getDlls()
    00797         if not dlls:
    00798             dlls = []
    00799         elif type(dlls) == types.StringType:
    00800             dlls = [ dlls ]
    00801
    00802         from __main__ import theApp
    00803         dlls = filter( lambda d: d not in theApp.Dlls, dlls )
    00804         if dlls: theApp.Dlls += dlls
    00805
    00806     def __setupDefaults( self ):
    00807         # set handle defaults flags to inform __setattr__ that it is being
    00808         # called during setDefaults of the concrete Configurable
    00809         self._inSetDefaults = True
    00810         self.setDefaults( self )
    00811         self._inSetDefaults = False
    00812
    00813     @staticmethod
    00814     def _printHeader( indentStr, title ):
    00815         preLen  = Configurable.printHeaderPre
    00816         postLen = Configurable.printHeaderWidth - preLen - 3 - len(title)# - len(indentStr)
    00817         postLen = max(preLen,postLen)
    00818         return indentStr + '/%s %s %s' % (preLen*'*',title,postLen*'*')
    00819
    00820     @staticmethod
    00821     def _printFooter( indentStr, title ):
    00822         preLen  = Configurable.printHeaderPre
    00823         postLen = Configurable.printHeaderWidth - preLen - 12 - len(title)# - len(indentStr)
    00824         postLen = max(preLen,postLen)
    00825         return indentStr + '\\%s (End of %s) %s' % (preLen*'-',title,postLen*'-')
    00826
    00827     def __repr__( self ):
    00828         return '<%s at %s>' % (self.getFullJobOptName(),hex(id(self)))
    00829
    00830     def __str__( self, indent = 0, headerLastIndentUnit=indentUnit ):
    00831         global log  # to print some info depending on output level
    00832         indentStr = indent*Configurable.indentUnit
    00833         # print header
    00834         title = self.getPrintTitle()
    00835         # print line to easily see start-of-configurable
    00836         if indent > 0:
    00837             headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
    00838         else:
    00839             headerIndent = ''
    00840         rep = Configurable._printHeader( headerIndent, title )
    00841         rep += os.linesep
    00842         # print own properties
    00843         props = self.getProperties()
    00844         defs = self.getDefaultProperties()
    00845         if not props:
    00846             rep += indentStr + '|-<no properties>' + os.linesep
    00847         else:
    00848             # get property name with
    00849             nameWidth = 0
    00850             for p in props.keys():
    00851                 nameWidth=max(nameWidth,len(p))
    00852             for p, v in props.items():
    00853                 # start with indent and property name
    00854                 prefix = indentStr + '|-%-*s' % (nameWidth,p)
    00855                 # add memory address for debugging (not for defaults)
    00856                 if log.isEnabledFor( logging.DEBUG ):
    00857                     if v != Configurable.propertyNoValue:
    00858                         address = ' @%11s' % hex(id(v))
    00859                     else:
    00860                         address = 13*' '
    00861                     prefix += address
    00862                 # add value and default
    00863                 default = defs.get(p)
    00864                 if v == Configurable.propertyNoValue:
    00865                     # show default value as value, and no extra 'default'
    00866                     strVal = repr(default)
    00867                     strDef = None
    00868                 else:
    00869                     # convert configurable to handle
    00870                     if hasattr(v,"getGaudiHandle"):
    00871                         vv = v.getGaudiHandle()
    00872                     else:
    00873                         vv = v
    00874                     if isinstance(vv,GaudiHandle) or isinstance(vv,GaudiHandleArray):
    00875                         strVal = repr(vv)
    00876                         if hasattr(default,"toStringProperty"): # the default may not be a GaudiHandle (?)
    00877                             strDef = repr(default.toStringProperty())
    00878                         else:
    00879                             strDef = repr(default)
    00880                         if strDef == repr(vv.toStringProperty()):
    00881                             strDef = None
    00882                     else:
    00883                         strVal = repr(vv)
    00884                         strDef = repr(default)
    00885                 # add the value
    00886                 line = prefix + ' = ' + strVal
    00887                 # add default if present
    00888                 if strDef is not None:
    00889                     # put default on new line if too big
    00890                     if len(line) + len(strDef) > Configurable.printHeaderWidth:
    00891                         line += os.linesep + indentStr + '| ' + (len(prefix)-len(indentStr)-3)*' '
    00892                     line += '  (default: %s)' % (strDef,)
    00893                 # add the line to the total string
    00894                 rep += line + os.linesep
    00895                 # print out full private configurables
    00896 ##              if isinstance(v,Configurable) and not v.isPublic():
    00897 ##                  rep += v.__str__( indent + 1 ) + os.linesep
    00898 ##              elif isinstance(v,GaudiHandleArray):
    00899 ##                  for vi in v:
    00900 ##                     if isinstance(vi,Configurable) and not vi.isPublic():
    00901 ##                         rep += vi.__str__( indent + 1 ) + os.linesep
    00902
    00903         # print configurables + their properties, or loop over sequence
    00904 ##      for cfg in self.__children:
    00905         for cfg in self.getAllChildren():
    00906             rep += cfg.__str__( indent + 1, '|=' ) + os.linesep
    00907
    00908         # print line to easily see end-of-configurable. Note: No linesep!
    00909         rep += Configurable._printFooter( indentStr, title )
    00910         return rep
    00911
    00912 ### classes for generic Gaudi component ===========
    00913 class DummyDescriptor( object ):
    00914     def __init__( self, name ):
    00915         self.__name__ = name        # conventional
    00916
    00917     def __get__( self, obj, type = None ):
    00918         return getattr( obj, self.__name__ )
    00919
    00920     def __set__( self, obj, value ):
    00921         object.__setattr__( obj, self.__name__, value )
    00922
    00923 class ConfigurableGeneric( Configurable ):
    00924     #__slots__ = { }
    00925
    00926     def __init__( self, name = Configurable.DefaultName ):
    00927         Configurable.__init__( self, name )
    00928         self._name = name
    00929         self._properties = {}
    00930
    00931     def __deepcopy__( self, memo ):
    00932         return self                 # algorithms are always shared
    00933
    00934     def getGaudiType( self ): return 'GenericComponent'
    00935     def getDlls( self ) : pass
    00936     def getHandle( self ) : pass
    00937
    00938     def __setattr__( self, name, value ):
    00939         # filter private (user) variables
    00940         if name[0] == '_':
    00941             super( ConfigurableGeneric, self ).__setattr__( name, value )
    00942             return
    00943
    00944         # filter configurable types
    00945         if isinstance( value, Configurable ):
    00946             self.__dict__[ name ] = value
    00947             return
    00948
    00949         # assume all the rest are properties
    00950         if not name in self._properties:
    00951             self._properties[ name ] = PropertyProxy( DummyDescriptor( name ) )
    00952         self._properties[ name ].__set__( self, value )
    00953
    00954     def getJobOptName( self ): return None
    00955
    00956
    00957 ### base classes for individual Gaudi algorithms/services/algtools ===========
    00958 class ConfigurableAlgorithm( Configurable ):
    00959     __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
    00960        'Enable' : 1, 'ErrorMax' : 1, 'ErrorCount' : 0, 'AuditAlgorithms' : 0, \
    00961        'AuditInitialize' : 0, 'AuditReinitialize' : 0, 'AuditExecute' : 0, \
    00962        'AuditFinalize' : 0, 'AuditBeginRun' : 0, 'AuditEndRun' : 0 }
    00963
    00964     def __init__( self, name = Configurable.DefaultName ):
    00965         super( ConfigurableAlgorithm, self ).__init__( name )
    00966         name = self.getName()
    00967         self._jobOptName = name[ name.find('/')+1 : ]   # strips class
    00968
    00969     def __deepcopy__( self, memo ):
    00970         return self                 # algorithms are always shared
    00971
    00972     def getHandle( self ):
    00973         return iAlgorithm( self.getJobOptName() )
    00974
    00975     def getGaudiType( self ):
    00976         return 'Algorithm'
    00977
    00978     def getJobOptName( self ):
    00979         return self._jobOptName
    00980
    00981
    00982 class ConfigurableService( Configurable ):
    00983     __slots__ = { 'OutputLevel' : 0, \
    00984        'AuditServices' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
    00985
    00986     def __deepcopy__( self, memo ):
    00987         return self                 # services are always shared
    00988
    00989     def copyChild( self, child ):
    00990         return child                # full sharing
    00991
    00992     def getHandle( self ):
    00993         return iService( self._name )
    00994
    00995     def getGaudiType( self ):
    00996         return 'Service'
    00997
    00998     def getGaudiHandle( self ):
    00999         return ServiceHandle( self.toStringProperty() )
    01000
    01001     def toStringProperty( self ):
    01002         # called on conversion to a string property for the jocat
    01003         return self.getName()
    01004
    01005
    01006 class ConfigurableAlgTool( Configurable ):
    01007     __slots__ = { '_jobOptName' : '', 'OutputLevel' : 0, \
    01008        'AuditTools' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
    01009
    01010     def __init__( self, name = Configurable.DefaultName ):
    01011         super( ConfigurableAlgTool, self ).__init__( name )
    01012         if '.' not in self._name:
    01013             # Public tools must have ToolSvc as parent
    01014             self._name = "ToolSvc." + self._name
    01015         name = self.getName()
    01016         name = name[ name.find('/')+1 : ]   # strips class, if any
    01017         self._jobOptName = name
    01018
    01019     def getHandle( self ):
    01020         # iAlgTool isn't useful, unless one knows for sure that the tool exists
    01021         return iProperty( self.getJobOptName() )
    01022
    01023     def getGaudiType( self ):
    01024         return 'AlgTool'
    01025
    01026     def getGaudiHandle( self ):
    01027         if self.isPublic():
    01028             return PublicToolHandle( self.toStringProperty() )
    01029         else:
    01030             return PrivateToolHandle( self.toStringProperty() )
    01031
    01032     def getPrintTitle(self):
    01033         if self.isPublic():
    01034             pop = 'Public '
    01035         else:
    01036             pop = 'Private '
    01037         return pop + Configurable.getPrintTitle(self)
    01038
    01039     def setParent( self, parentName ):
    01040 #       print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
    01041 #       print "Calling stack:"
    01042 #       import traceback
    01043 #       traceback.print_stack()
    01044         # propagate parent to AlgTools in children
    01045         for c in self.getAllChildren():
    01046             if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
    01047
    01048         # update my own parent
    01049         name = self.getName()
    01050         name = name[name.rfind('.')+1:] # Name of the instance
    01051         self._jobOptName = self._name = parentName + '.' + name
    01052
    01053     def getParent( self ):
    01054         dot = self._jobOptName.rfind('.')
    01055         if dot != -1:
    01056             return self._jobOptName[:dot]
    01057         else:
    01058             return ""
    01059
    01060     def hasParent( self, parent ):
    01061         return self._jobOptName.startswith( parent + '.' )
    01062
    01063     def getJobOptName( self ):
    01064         return self._jobOptName
    01065
    01066     def isPublic( self ):
    01067         return self.isInToolSvc()
    01068
    01069     def isInToolSvc( self ):
    01070         return self._jobOptName.startswith('ToolSvc.')
    01071
    01072     def toStringProperty( self ):
    01073         # called on conversion to a string property for the jocat
    01074         return self.getFullName()
    01075
    01076     def getFullName( self ) :
    01077         # for Tools, the "full name" means "Type/LocalName",
    01078         # without the names of the parents
    01079         name = self.getName()
    01080         # strip off everything before the last '.'
    01081         name = name[name.rfind('.')+1:]
    01082         return str( self.getType() + '/' + name )
    01083
    01084
    01085 ### FIXME: this is just a placeholder, waiting for a real implementation
    01086 ###        It is sufficient to get us going... (and import a PkgConf which
    01087 ###        happens to contain an Auditor...)
    01088 class ConfigurableAuditor( Configurable ):
    01089     __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
    01090                   'Enable' : 1 }
    01091
    01092     def __init__( self, name = Configurable.DefaultName ):
    01093         super( ConfigurableAuditor, self ).__init__( name )
    01094         name = self.getName()
    01095         name = name[ name.find('/')+1 : ]   # strips class, if any
    01096         self._jobOptName = name
    01097
    01098     def getHandle( self ):
    01099         # iAlgTool isn't useful, unless one knows for sure that the tool exists
    01100         return iProperty( self.getJobOptName() )
    01101
    01102     def getGaudiType( self ):
    01103         return 'Auditor'
    01104
    01105     def getJobOptName( self ):
    01106         return self._jobOptName
    01107
    01108     def toStringProperty( self ):
    01109     # called on conversion to a string property for the jocat
    01110         return self.getType() + '/' + self.getName()
    01111
    01112 class ConfigurableUser( Configurable ):
    01113     __slots__ = { "__users__": [],
    01114                   "__used_instances__": [],
    01115                   "_enabled": True }
    01116     ## list of ConfigurableUser classes this one is going to modify in the
    01117     #  __apply_configuration__ method.
    01118     #  The list may contain class objects, strings representing class objects or
    01119     #  tuples with the class object (or a string) as first element and the instance
    01120     #  name as second element.
    01121     #  If the instance name is None or not present, the function _instanceName()
    01122     #  is used to determine the name of the instance (the default implementation
    01123     #  returns "<this name>_<other name>".
    01124     __used_configurables__ = []
    01125     ## list of ConfigurableUser classes this one is going to query in the
    01126     #  __apply_configuration__ method
    01127     __queried_configurables__ = []
    01128     def __init__( self, name = Configurable.DefaultName, _enabled = True, **kwargs ):
    01129         super( ConfigurableUser, self ).__init__( name )
    01130         for n, v in kwargs.items():
    01131             setattr(self, n, v)
    01132         self._enabled = _enabled
    01133         self.__users__ = []
    01134
    01135         # Needed to retrieve the actual class if the declaration in __used_configurables__
    01136         # and  __queried_configurables__ is done with strings.
    01137         from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
    01138
    01139         # Set the list of users of the used configurables
    01140         #
    01141         self.__used_instances__ = []
    01142         for used in self.__used_configurables__:
    01143             # By default we want to use the default name of the instances
    01144             # for the used configurables
    01145             used_name = Configurable.DefaultName
    01146             # If the entry in the list is a tuple, we need a named instance
    01147             if type(used) is tuple:
    01148                 used, used_name = used # we re-set used to re-use the code below
    01149                 if not used_name:
    01150                     used_name = self._instanceName(used)
    01151             # Check is 'used' is a string or not
    01152             if type(used) is str:
    01153                 used_class = confDbGetConfigurable(used)
    01154             else:
    01155                 used_class = used
    01156             # Instantiate the configurable that we are going to use
    01157             try:
    01158                 inst = used_class(name = used_name, _enabled = False)
    01159             except AttributeError:
    01160                 # This cover the case where the used configurable is not a
    01161                 # ConfigurableUser instance, i.e. id doesn't have the attribute
    01162                 # '_enabled'.
    01163                 inst = used_class(name = used_name)
    01164             self.__addActiveUseOf(inst)
    01165         for queried in self.__queried_configurables__:
    01166             try:
    01167                 if type(queried) is str:
    01168                     queried = confDbGetConfigurable(used)
    01169                 inst = queried(_enabled = False)
    01170             except AttributeError:
    01171                 inst = queried()
    01172             self.__addPassiveUseOf(inst)
    01173     def __addActiveUseOf(self, other):
    01174         """
    01175         Declare that we are going to modify the Configurable 'other' in our
    01176         __apply_configuration__.
    01177         """
    01178         self.__used_instances__.append(other)
    01179         if hasattr(other, "__users__"): # allow usage of plain Configurables
    01180             other.__users__.append(self)
    01181     def __addPassiveUseOf(self, other):
    01182         """
    01183         Declare that we are going to retrieve property values from the
    01184         ConfigurableUser 'other' in our __apply_configuration__.
    01185         """
    01186         if not isinstance(other, ConfigurableUser):
    01187             raise Error("'%s': Cannot make passive use of '%s', it is not a ConfigurableUser" % (self.name(), other.name()))
    01188         other.__addActiveUseOf(self)
    01189     def getGaudiType( self ):
    01190         return 'User'
    01191     def getDlls( self ):
    01192         return None
    01193     def getHandle( self ):
    01194         return None
    01195
    01196     def __detach_used__(self):
    01197         """
    01198         Remove this ConfigurableUser instance from the users list of the used
    01199         instances.
    01200         """
    01201         for used in self.__used_instances__:
    01202             if hasattr(used, "__users__"): # allow usage of plain Configurables
    01203                 used.__users__.remove(self)
    01204
    01205     def propagateProperty(self, name, others = None, force = True):
    01206         """
    01207         Propagate the property 'name' (if set) to other configurables (if possible).
    01208         'others' can be:
    01209             None:
    01210                 propagate to all the entries in __used_configurables__
    01211             a configurable instance:
    01212                 propagate only to it
    01213             list of configurable instances:
    01214                 propagate to all of them.
    01215
    01216
    01217         The logic is:
    01218         - if the local property is set, the other property will be overwritten
    01219         - local property not set and other set => keep other
    01220         - local property not set and other not set => overwrite the default for
    01221             ConfigurableUser instances and set the property for Configurables
    01222         """
    01223         # transform 'others' to a list of configurable instances
    01224         if others is None:
    01225             others = self.__used_instances__
    01226         elif type(others) not in [ list, tuple ] :
    01227             others = [ others ]
    01228         # these can be computed before the loop
    01229         local_is_set = self.isPropertySet(name)
    01230         value = self.getProp(name)
    01231         # loop over the others that do have 'name' in their slots
    01232         for other in [ o for o in others if name in o.__slots__ ]:
    01233             # If self property is set, use it
    01234             if local_is_set:
    01235                 if other.isPropertySet(name):
    01236                     log.warning("Property '%(prop)s' is set in both '%(self)s' and '%(other)s', using '%(self)s.%(prop)s'"%
    01237                                 { "self": self.name(),
    01238                                   "other": other.name(),
    01239                                   "prop": name } )
    01240                 other.setProp(name, value)
    01241             # If not, and other property also not set, propagate the default
    01242             elif not other.isPropertySet(name):
    01243                 if isinstance(other,ConfigurableUser):
    01244                     otherType = type(other._properties[name].getDefault())
    01245                     other._properties[name].setDefault(value)
    01246                     if otherType in [list, dict]:
    01247                         # Special case for list and dictionaries:
    01248                         # also set the property to the same value of the default (copy)
    01249                         other.setProp(name, otherType(value))
    01250                 else:
    01251                     other.setProp(name, value)
    01252             # If not set and other set, do nothing
    01253
    01254     def propagateProperties(self, names = None, others = None, force = True):
    01255         """
    01256         Call propagateProperty for each property listed in 'names'.
    01257         If 'names' is None, all the properties are propagated.
    01258         """
    01259         if names is None:
    01260             # use all the non-private slots
    01261             names = [ p for p in self.__slots__ if not p.startswith("_") ]
    01262         for n in names:
    01263             self.propagateProperty(n, others, force)
    01264
    01265     def __apply_configuration__(self):
    01266         """
    01267         Function to be overridden to convert the high level configuration into a
    01268         low level one.
    01269         The default implementation calls applyConf, which is the method defined
    01270         in some ConfigurableUser implementations.
    01271         """
    01272         return self.applyConf()
    01273
    01274     def applyConf( self ):
    01275         """
    01276         Function to be overridden to convert the high level configuration into a
    01277         low level one.
    01278         """
    01279         pass
    01280
    01281     def _instanceName(self, cls):
    01282         """
    01283         Function used to define the name of the private instance of a given class
    01284         name.
    01285         This method is used when the __used_configurables_property__ declares the
    01286         need of a private used configurable without specifying the name.
    01287         """
    01288         if type(cls) is str:
    01289             clName = cls
    01290         else:
    01291             clName = cls.__name__
    01292         return "%s_%s" % (self.name(), clName)
    01293
    01294     def getUsedInstance(self, name):
    01295         """
    01296         Return the used instance with a given name.
    01297         """
    01298         for i in self.__used_instances__:
    01299             if i.name() == name:
    01300                 if hasattr(i, "_enabled"):
    01301                     # ensure that the instances retrieved through the method are
    01302                     # enabled
    01303                     i._enabled = True
    01304                 return i
    01305         raise KeyError(name)
    01306
    01307 # list of callables to be called after all the __apply_configuration__ are called.
    01308 postConfigActions = []
    01309 def appendPostConfigAction(function):
    01310     """
    01311     Add a new callable ('function') to the list of post-configuration actions.
    01312     If the callable is already in the list, it is moved to the end of the list.
    01313     The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
    01314     """
    01315     try:
    01316         postConfigActions.remove(function)
    01317     except:
    01318         pass
    01319     postConfigActions.append(function)
    01320 def removePostConfigAction(function):
    01321     """
    01322     Remove a collable from the list of post-config actions.
    01323     The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
    01324     """
    01325     postConfigActions.remove(function)
    01326
    01327 _appliedConfigurableUsers_ = False
    01328 def applyConfigurableUsers():
    01329     """
    01330     Call the apply method of all the ConfigurableUser instances respecting the
    01331     dependencies. First the C.U.s that are not used by anybody, then the used
    01332     ones, when they are not used anymore.
    01333     """
    01334     # Avoid double calls
    01335     global _appliedConfigurableUsers_, postConfigActions
    01336     if _appliedConfigurableUsers_:
    01337         return
    01338     _appliedConfigurableUsers_ = True
    01339
    01340     confUsers = [ c
    01341                   for c in Configurable.allConfigurables.values()
    01342                   if hasattr(c,"__apply_configuration__") ]
    01343     applied = True # needed to detect dependency loops
    01344     while applied and confUsers:
    01345         newConfUsers = [] # list of conf users that cannot be applied yet
    01346         applied = False
    01347         for c in confUsers:
    01348             if hasattr(c,"__users__") and c.__users__:
    01349                 newConfUsers.append(c) # cannot use this one yet
    01350             else: # it does not have users or the list is empty
    01351                 applied = True
    01352                 # the ConfigurableUser is enabled if it doesn't have an _enabled
    01353                 # property or its value is True
    01354                 enabled = (not hasattr(c, "_enabled")) or c._enabled
    01355                 if enabled:
    01356                     log.info("applying configuration of %s", c.name())
    01357                     c.__apply_configuration__()
    01358                     log.info(c)
    01359                 else:
    01360                     log.info("skipping configuration of %s", c.name())
    01361                 if hasattr(c, "__detach_used__"):
    01362                     # tells the used configurables that they are not needed anymore
    01363                     c.__detach_used__()
    01364         confUsers = newConfUsers # list of C.U.s still to go
    01365     if confUsers:
    01366         # this means that some C.U.s could not be applied because of a dependency loop
    01367         raise Error("Detected loop in the ConfigurableUser "
    01368                     " dependencies: %r" % [ c.name()
    01369                                             for c in confUsers ])
    01370     # Call post-config actions
    01371     for action in postConfigActions:
    01372         action()
    01373
    01374 def getNeededConfigurables():
    01375     """
    01376     Function to select all and only the configurables that have to be used in
    01377     GaudiPython.AppMgr constructor.
    01378     This is needed because in Athena the implementation have to be different (the
    01379     configuration is used in a different moment).
    01380     """
    01381     return [ k
    01382              for k, v in Configurable.allConfigurables.items()
    01383              if v.getGaudiType() != "User" ] # Exclude ConfigurableUser instances
    01384
    01385 def purge():
    01386     """
    01387     Clean up all configurations and configurables.
    01388     """
    01389     for c in Configurable.allConfigurables.values():
    01390         c.__class__.configurables.clear()
    01391     Configurable.allConfigurables.clear()
    01392     # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
    01393     #        migrated to the correct class when this is known.
    01394     ConfigurableGeneric.configurables.clear()
    01395     from ProcessJobOptions import _included_files
    01396     import os.path, sys
    01397     for file in _included_files:
    01398         dirname, basname = os.path.split(file)
    01399         basname, ext = os.path.splitext(basname)
    01400         if basname in sys.modules:
    01401             del sys.modules[basname]
    01402     _included_files.clear()