| # Gaudi Framework, version v21r6 |

Home |  | Generated: 11 Nov 2009

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