All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Configurable.py
Go to the documentation of this file.
1 # File: AthenaCommon/python/Configurable.py
2 # Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
3 # Author: Martin Woudstra (Martin.Woudstra@cern.ch)
4 
5 import copy, string, types, os
6 import sys
7 from inspect import isclass
8 import GaudiKernel.ConfigurableMeta as ConfigurableMeta
9 from GaudiKernel.Constants import error_explanation, \
10  VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL
11 from GaudiKernel.PropertyProxy import PropertyProxy
12 from GaudiKernel.GaudiHandles import *
13 
14 ### data ---------------------------------------------------------------------
15 __all__ = [ 'Configurable',
16  'ConfigurableAlgorithm',
17  'ConfigurableAlgTool',
18  'ConfigurableAuditor',
19  'ConfigurableService',
20  'ConfigurableUser',
21  'VERBOSE','DEBUG','INFO', 'WARNING', 'ERROR', 'FATAL',
22  'appendPostConfigAction', 'removePostConfigAction' ]
23 
24 ## for messaging
25 import logging
26 log = logging.getLogger( 'Configurable' )
27 
28 def expandvars(data):
29  """
30  Expand environment variables "data".
31  Data can be string, list, tuple and dictionary. For collection, all the
32  contained strings will be manipulated (recursively).
33  """
34  import os.path
35  typ = type(data)
36  if typ is str:
37  return os.path.expandvars(data)
38  elif typ in [list, tuple]:
39  collect = []
40  for i in data:
41  collect.append(expandvars(i))
42  return typ(collect)
43  elif typ is dict:
44  collect = {}
45  for k in data:
46  collect[expandvars(k)] = expandvars(data[k])
47  return collect
48  return data
49 
50 class Error(RuntimeError):
51  """
52  Error occurred in the configuration process.
53  """
54  pass
55 
56 ## Allow references to options as in old style
57 class PropertyReference(object):
58  def __init__(self,propname):
59  self.name = propname
60  def __repr__(self):
61  return "@%s"%self.name
62  def __resolve__(self):
63  # late binding for property references
64  retval = None
65  refname, refprop = self.name.rsplit('.',1)
66  if refname in Configurable.allConfigurables:
67  conf = Configurable.allConfigurables[refname]
68  retval = getattr(conf,refprop)
69  if hasattr(retval,"getFullName"):
70  retval = retval.getFullName()
71  else:
72  raise NameError("name '%s' not found resolving '%s'"%(refname,self))
73  return retval
74  def getFullName(self):
75  """This function allow transparent integration with
76  Configurable.getValuedProperties.
77  """
78  try:
79  return self.__resolve__()
80  except NameError:
81  # ignore the error if we cannot resolve the name yet
82  return self
83  except AttributeError:
84  # ignore the error if we cannot resolve the attribute yet
85  return self
86 
87 ### base class for configurable Gaudi algorithms/services/algtools/etc. ======
88 class Configurable( object ):
89  """Base class for Gaudi components that implement the IProperty interface.
90  Provides most of the boilerplate code, but the actual useful classes
91  are its derived ConfigurableAlgorithm, ConfigurableService, and
92  ConfigurableAlgTool."""
93 
94  ## for detecting the default name
95  class DefaultName:
96  pass
97 
98  propertyNoValue = '<no value>'
99  indentUnit = '| '
100  printHeaderWidth=100
101  printHeaderPre=5
102 
104 
105  __slots__ = (
106  '__children', # controlled components, e.g. private AlgTools
107  '__tools', # private AlgTools (#PM-->)
108  '_name', # the (unqualified) component name
109  '_inSetDefaults', # currently setting default values
110  '_initok', # used to enforce base class init
111  '_setupok' # for debugging purposes (temporary)
112  )
113 
114  allConfigurables = {} # just names would do, but currently refs to the actual
115  configurableServices = {} # just names would do, but currently refs to the actual
116  # configurables is needed for (temporary) backwards
117  # compatibility; will change in the future
118  _configurationLocked = False
119 
120  def __new__ ( cls, *args, **kwargs ):
121  """To Gaudi, any object with the same type/name is the same object. Hence,
122  this is mimicked in the configuration: instantiating a new Configurable
123  of a type with the same name will return the same instance."""
124 
125  global log
126  # try to get the name of the Configurable (having a name is compulsory)
127  if 'name' in kwargs:
128  # simple keyword (by far the easiest)
129  name = kwargs[ 'name' ]
130  elif 'name' in cls.__init__.func_code.co_varnames:
131  # either positional in args, or default
132  index = list(cls.__init__.func_code.co_varnames).index( 'name' )
133  try:
134  # var names index is offset by one as __init__ is to be called with self
135  name = args[ index - 1 ]
136  except IndexError:
137  # retrieve default value, then
138  name = cls.__init__.func_defaults[ index - (len(args)+1) ]
139  else:
140  # positional index is assumed (will work most of the time)
141  try:
142  name = args[1] # '0' is for self
143  except (IndexError,TypeError):
144  raise TypeError( 'no "name" argument while instantiating "%s"' % cls.__name__ )
145 
146  argname = name
147  if name == Configurable.DefaultName :
148  if hasattr(cls, 'DefaultedName' ) :
149  name = cls.DefaultedName
150  else :
151  name = cls.getType()
152  elif not name or type(name) != str:
153  # unnamed, highly specialized user code, etc. ... unacceptable
154  raise TypeError( 'could not retrieve name from %s.__init__ arguments' % cls.__name__ )
155 
156  # Handle the case of global tools to prepend ToolSvc in the name.
157  # This is needed for compatibility with old JobOptions files being read
158  if issubclass( cls, ConfigurableAlgTool) and '.' not in name :
159  name = 'ToolSvc.' + name
160 
161  # close backdoor access to otherwise private subalgs/tools
162  #PM if 0 <= name.find( '.' ):
163  #PM # temp protection for old style types
164  #PM from OldStyleConfig import GenericConfigurable
165  #PM if not issubclass( cls, GenericConfigurable ): # except raised for new types only
166  #PM raise NameError( '"%s": backdoor access to private configurables not allowed' % name )
167 
168  # ordinary recycle case
169  if name in cls.configurables:
170  conf = cls.configurables[ name ]
171  if name != argname: # special case: user derived <-> real ... make same
172  cls.configurables[ conf.getType() ] = conf
173  #---PM: Initialize additional properties
174  for n,v in kwargs.items():
175  if n != "name": # it should not be confused with a normal property
176  setattr(conf, n, v)
177  if not cls._configurationLocked and not "_enabled" in kwargs and isinstance(conf, ConfigurableUser):
178  # Ensure that the ConfigurableUser gets enabled if nothing is
179  # specified in the constructor.
180  setattr(conf, "_enabled", True)
181  return conf
182 
183  # a couple of special cases (note that these cases don't mix)
184  spos = name.find( '/' )
185  ti_name = None
186  if spos < 0:
187  ti_name = "%s/%s" % (name,name)
188  if ti_name in cls.configurables:
189  # support for old-style name as type/name lookup where name==type
190  return cls.configurables[ ti_name ]
191 
192  i_name = None
193  if spos > 0:
194  i_name = name[:spos]
195  if i_name == name[spos+1:] and i_name in cls.configurables:
196  # this is the opposite of the above special case
197  return cls.configurables[ i_name ]
198 
199  # the following is purely for debugging support and should realistically bomb
200  conf = cls.allConfigurables.get( name, None ) or\
201  (spos < 0 and cls.allConfigurables.get( ti_name, None )) or\
202  (spos > 0 and i_name == name[spos+1:] and cls.allConfigurables.get( i_name, None ))
203  if conf: # wrong type used?
204  if conf.__class__ is ConfigurableGeneric :
205  # If the instance found is ConfigurableGeneric then
206  # we create a new one with the proper type and fill with
207  # the contents of the generic one
208  newconf = object.__new__( cls )
209  cls.__init__( newconf, *args, **kwargs )
210  # initialize with the properties of generic configurable
211  # (we map the names of the properties to lowercase versions because
212  # old options are not case sensitive)
213  names = {}
214  for n in newconf.__slots__:
215  names[n.lower()] = n
216  for n in conf._properties:
217  if names[n.lower()] != n:
218  log.warning( "Option '%s' was used for %s, but the correct spelling is '%s'"%(n,name,names[n.lower()]) )
219  setattr(newconf, names[n.lower()], getattr( conf, n ) )
220  for n,v in kwargs.items():
221  setattr(newconf, n, v)
222  cls.configurables[ name ] = newconf
223  cls.allConfigurables[ name ] = newconf
224  return newconf
225  else :
226  # will be an actual error in the future (now only report as such)
227  log.error( 'attempt to redefine type of "%s" (was: %s, new: %s)%s',
228  name, conf.__class__.__name__, cls.__name__, error_explanation )
229  # in the future:
230  # return None # will bomb on use (or go unharmed on non-use)
231  # for now, allow use through allConfigurables lookup
232  #---PM: Initialize additional properties
233  for n,v in kwargs.items():
234  setattr(conf, n, v)
235  return conf
236 
237  # still here: create a new instance and initialize it
238  conf = object.__new__(cls)
239  cls.__init__( conf, *args, **kwargs )
240 
241  # update normal, per-class cache
242  cls.configurables[ name ] = conf
243 
244  for base in cls.__bases__:
245  if base.__name__ == 'ConfigurableService':
246  cls.configurableServices[ name ] = conf
247 
248  # update generics super-cache, if needed
249  cls.allConfigurables[ name ] = conf
250  #-->PM#if hasattr( cls, 'getType' ) and name.find('/') < 0:
251  #-->PM# cls.allConfigurables[ cls.getType() + '/' + name ] = conf
252 
253  return conf
254 
255  def __init__( self, name = DefaultName ):
256  # check class readiness, all required overloads should be there now
257  klass = self.__class__
258 
259  # this is an abstract class
260  if klass == Configurable:
261  raise TypeError, "%s is an ABC and can not be instantiated" % str(Configurable)
262 
263  # the following methods require overloading
264  #NOT YET meths = { 'getServices' : 1, # retrieve list of services to configure
265  meths = { 'getDlls' : 1, # provide list of Dlls to load
266  'getGaudiType' : 1, # return string describing component class
267  'getHandle' : 1 } # provide access to C++ side component instance
268 # 'getType' : 1 } # return the type of the actual C++ component
269 
270  for meth, nArgs in meths.items():
271  try:
272  f = getattr( klass, meth ).im_func
273  except AttributeError:
274  raise NotImplementedError, "%s is missing in class %s" % (meth,str(klass))
275 
276  # in addition, verify the number of arguments w/o defaults
277  nargcount = f.func_code.co_argcount
278  ndefaults = f.func_defaults and len(f.func_defaults) or 0
279  if not nargcount - ndefaults <= nArgs <= nargcount:
280  raise TypeError, "%s.%s requires exactly %d arguments" % (klass,meth,nArgs)
281 
282  # for using this Configurable as a (Gaudi) sequence
283  self.__children = []
284  self.__tools = {}
285 
286  # know who we are
287  if name == Configurable.DefaultName :
288  if hasattr(self.__class__, 'DefaultedName' ) :
289  self._name = self.__class__.DefaultedName
290  else :
291  self._name = self.getType()
292  else :
293  self._name = name
294 
295  # set to True when collecting defaults, False otherwise
296  self._inSetDefaults = False
297 
298  # for later, in case __init__ itself is overridden
299  self._initok = True
300 
301  # for debugging purposes (temporary)
302  self._setupok = False
303 
304  # pickle support
305  def __getstate__ (self):
306  dict = {}
307  for name, proxy in self._properties.items():
308  try:
309  dict[ name ] = proxy.__get__( self )
310  except AttributeError:
311  pass
312 
313  dict[ '_Configurable__children' ] = self.__children
314  dict[ '_Configurable__tools' ] = self.__tools
315  dict[ '_name' ] = self._name
316  return dict
317 
318  def __getnewargs__(self) :
319  return (self._name,)
320 
321  def __setstate__ ( self, dict ):
322  self._initok = True
323  for n, v in dict.items():
324  setattr (self, n, v)
325  return
326 
327  # to allow a few basic sanity checks, as well as nice syntax
328  def __len__( self ):
329  return len( self.__children )
330 
331  def __iter__( self ):
332  return iter( self.__children )
333 
334  # ownership rules of self through copying
335  def __deepcopy__( self, memo ):
336  newconf = object.__new__( self.__class__ )
337  self.__class__.__init__( newconf, self.getName() )
338 
339  for proxy in self._properties.values():
340  try:
341  proxy.__set__( newconf, proxy.__get__( self ) )
342  except AttributeError:
343  pass # means property was not set for self
344 
345  for c in self.__children:
346  newconf += c # processes proper copy semantics
347 
348  return newconf
349 
350  # hierarchy building, and ownership rules of children
351  def __iadd__( self, configs, descr = None ):
352  if not type(configs) in (list,tuple):
353  configs = ( configs, )
354 
355  joname = self.getJobOptName()
356 
357  for cfg in configs:
358  # prevent type mismatches
359  if not isinstance( cfg, Configurable ):
360  raise TypeError( "'%s' is not a Configurable" % str(cfg) )
361 
362  cc = self.copyChildAndSetParent( cfg, joname )
363 
364  # filters dupes; usually "ok" (backdoor should catch them)
365  ccjo = cc.getJobOptName()
366  for c in self.__children:
367  if c.getJobOptName() == ccjo:
368  log.error( 'attempt to add a duplicate ... dupe ignored%s', error_explanation )
369  break
370  else:
371  self.__children.append( cc )
372 
373  try:
374  if descr: # support for tool properties
375  descr.__set__( self, cc )
376  else:
377  setattr( self, cc.getName(), cc )
378  except AttributeError:
379  pass # to allow free addition of tools/subalgorithms
380 
381  return self
382 
383  def __getattr__( self, attr ): # until ToolProperties exist ...
384 
385  if attr in self.__tools : return self.__tools[attr]
386 
387  for c in self.__children:
388  if c.getName() == attr:
389  return c
390 
391  raise AttributeError( "'%s' object has no attribute '%s'" % (self.__class__,attr) )
392 
393  def __setattr__( self, name, value ) :
394  if self._configurationLocked:
395  raise RuntimeError("%s: Configuration cannot be modified after the ApplicationMgr has been started."%self.name())
396  try :
397  super( Configurable, self ).__setattr__( name, value )
398  except AttributeError:
399  raise AttributeError( "Configurable '%s' does not have property '%s'."
400  % ( self.__class__.__name__, name) )
401 
402  def __delattr__( self, attr ):
403  # remove as property, otherwise try as child
404  try:
405  # remove history etc., then reset to default (in case set before)
406  prop = self._properties[ attr ]
407  prop.__delete__( self )
408  prop.__set__( self, prop.default )
409  return # reaches here? was property: done now
410  except KeyError:
411  pass
412  # otherwise, remove the private tool
413  if attr in self.__tools :
414  del self.__tools[attr]
415 
416  # otherwise, remove child, if one is so named
417  for c in self.__children:
418  if c.getName() == attr:
419  self.__children.remove( c )
420 
421  # potentially, there are left over caches (certain user derived classes)
422  try:
423  del self.__dict__[ attr ]
424  except (AttributeError,KeyError):
425  pass
426 
427  def __nonzero__(self):
428  return True
429 
430 
431  def remove( self, items ):
432  if type(items) != list and type(items) != tuple:
433  items = [ items ]
434 
435  self.__children = [ e for e in self.__children if not e in items ]
436 
437  def removeAll( self ):
438  self.remove( self.__children )
439 
440  # called by __iadd__; determines child copy semantics
441  def copyChild( self, child ):
442  return copy.deepcopy( child )
443 
444  def setParent( self, parentName ):
445  pass
446 
447  def getParent( self ):
448  return ""
449 
450  def hasParent( self, parent ):
451  return False
452 
453  def copyChildAndSetParent(self,cfg,parent):
454  cc = self.copyChild( cfg )
455 
456  if hasattr( cc, 'setParent' ) and parent:
457  try:
458  cc.setParent( parent )
459  except RuntimeError, e:
460  # temporary backdoor resolution for compatibility
461  log.error( str(e) + '%s', error_explanation )
462  ccbd = cc.configurables[ cc.getJobOptName() ]
463 
464  # merge properties, new over pre-existing
465  for proxy in self._properties.values():
466  if proxy.history.has_key( cc ):
467  proxy.__set__( ccbd, proxy.__get__( cc ) )
468 
469  # consolidate
470  cc = ccbd
471  return cc
472 
473  def getChildren( self ):
474  return self.__children[:] # read only
475 
476  def getTools( self ):
477  return self.__tools.values() # read only
478 
479  def children( self ):
480  log.error( "children() is deprecated, use getChildren() instead for consistency" )
481  log.error( "getChildren() returns a copy; to add a child, use 'parent += child'%s",
482  error_explanation )
483  return self.__children # by ref, for compatibility
484 
485  def getAllChildren( self ):
486  """Get all (private) configurable children, both explicit ones (added with +=)
487  and the ones in the private GaudiHandle properties"""
488  childs = []
489  # add private configurable properties (also inside handles)
490  for proxy in self._properties.values():
491  try:
492  c = proxy.__get__( self )
493  except AttributeError:
494  pass
495  else:
496  if isinstance(c,Configurable) and not c.isPublic():
497  childs.append(c)
498  elif isinstance(c,GaudiHandle):
499  try:
500  conf = c.configurable
501  except AttributeError:
502  pass
503  else:
504  if not conf.isPublic():
505  childs.append(conf)
506  elif isinstance(c,GaudiHandleArray):
507  # only setup private arrays
508  if not c.isPublic():
509  for ci in c:
510  if isinstance(ci,Configurable):
511  childs.append(ci)
512  else:
513  try:
514  conf = ci.configurable
515  except AttributeError:
516  pass
517  else:
518  childs.append(conf)
519 
520  # add explicit children
521  childs += self.__children
522  return childs
523 
524  def getSequence( self ):
525  elems = []
526  for c in self.__children:
527  elems.append( c.getFullName() )
528  return elems
529 
530  def setup( self ):
531  # make sure base class init has been called
532  if not hasattr(self,'_initok') or not self._initok:
533  # could check more, but this is the only explanation
534  raise TypeError, \
535  "Configurable.__init__ not called in %s override" % self.__class__.__name__
536 
537 # log.debug("calling setup() on " + self.getFullJobOptName())
538 
539  # setup self: this collects all values on the python side
540  self.__setupServices()
541  self.__setupDlls()
542  self.__setupDefaults()
543 
544  # setup children
545  for c in self.getAllChildren():
546  c.setup()
547 
548  # now get handle to work with for moving properties into the catalogue
549  handle = self.getHandle()
550  if not handle:
551  log.debug( 'no handle for %s: not transporting properties', self._name )
552  return # allowed, done early
553 
554  # pass final set of properties on to handle on the C++ side or JobOptSvc
555  for name in self._properties.keys():
556  if hasattr( self, name ): # means property has python-side value/default
557  setattr( handle, name, getattr(self,name) )
558 
559  # for debugging purposes
560  self._setupok = True
561 
562  def getProperties( self ):
563  props = {}
564  for name, proxy in self._properties.items():
565  try:
566  props[ name ] = proxy.__get__( self )
567  except AttributeError:
568  props[ name ] = Configurable.propertyNoValue
569 
570  return props
571 
572  def getValuedProperties( self ):
573  props = {}
574  for name, proxy in self._properties.items():
575  if self.isPropertySet(name):
576  value = proxy.__get__( self )
577  if hasattr(value, 'getFullName') :
578  value = value.getFullName()
579  elif type(value) in [list, tuple]:
580  new_value = []
581  for i in value:
582  if hasattr(i, 'getFullName'):
583  new_value.append(i.getFullName())
584  else:
585  new_value.append(i)
586  value = type(value)(new_value)
587  elif type(value) is dict:
588  new_value = {}
589  for i in value:
590  if hasattr(value[i], 'getFullName'):
591  new_value[i] = value[i].getFullName()
592  else:
593  new_value[i] = value[i]
594  value = new_value
595  props[ name ] = value
596 
597  return props
598 
599  def properties( self ):
600  return self.getProperties() # compatibility
601 
602  @classmethod
604  class collector:
605  pass
606 
607  # user provided defaults
608  c = collector()
609  cls.setDefaults( c )
610 
611  # defaults from C++
612  for k,v in cls._properties.items():
613  if not k in c.__dict__ and hasattr( v, 'default' ):
614  c.__dict__[ k ] = v.default
615 
616  return c.__dict__
617 
618  @classmethod
619  def getDefaultProperty( cls, name ):
620  class collector:
621  pass
622 
623  # user provided defaults
624  c = collector()
625  cls.setDefaults( c )
626 
627  if name in c.__dict__:
628  return c.__dict__[ name ]
629 
630  # defaults from C++
631  try:
632  v = cls._properties[name]
633  if hasattr( v, 'default' ):
634  return v.default
635  except KeyError:
636  pass
637 
638  return None
639 
640  def getProp(self, name):
641  """Returns the value of the given property.
642  """
643  if hasattr(self, name):
644  return getattr(self, name)
645  else:
646  return self.getDefaultProperties()[name]
647 
648  def setProp(self, name, value):
649  """Set the value of a given property
650  """
651  return setattr(self, name, value)
652 
653  def isPropertySet(self, name):
654  """Tell if the property 'name' has been set or not.
655 
656  Because of a problem with list and dictionary properties, in those cases
657  if the value is equal to the default, the property is considered as not
658  set.
659  """
660  if not hasattr(self, name):
661  return False
662  else:
663  try:
664  default = self.getDefaultProperties()[name]
665  if isinstance(default, (list, dict)):
666  value = getattr(self, name)
667  return value != default
668  except KeyError:
669  pass # no default found
670  return True
671 
672  def getType( cls ):
673  return cls.__name__
674 
675  def getName( self ):
676  return self._name
677 
678  def name( self ):
679  return self.getName()
680 
681  def getJobOptName( self ): # full hierachical name
682  return self.getName()
683 
684  def isPublic( self ):
685  return True
686 
687  # for a couple of existing uses out there
688  def jobOptName( self ):
689  log.error( "jobOptName() is deprecated, use getJobOptName() instead for consistency%s",
690  error_explanation )
691  return self.getJobOptName() # compatibility
692 
693  def getFullName( self ) :
694  return str( self.getType() + '/' + self.getName() )
695 
696  def getFullJobOptName( self ):
697  return "%s/%s" % (self.getType(),self.getJobOptName() or self.getName())
698 
699  def getPrintTitle(self):
700  return self.getGaudiType() + ' ' + self.getTitleName()
701 
702  def getTitleName( self ):
703  if log.isEnabledFor( logging.DEBUG ):
704  return self.getFullJobOptName()
705  else:
706  return self.getFullName()
707 
708  def setDefaults( cls, handle ):
709  pass
710 
711  def clone( self, name = None, **kwargs ) :
712  if not name :
713  if hasattr(self, 'DefaultedName' ) : name = self.DefaultedName
714  else : name = self.getType()
715 
716  newconf = Configurable.__new__( self.__class__, name )
717  self.__class__.__init__( newconf, name )
718 
719  for proxy in self._properties.values():
720  try :
721  value = proxy.__get__( self )
722  if type(value) in [ str, list, dict, tuple ]:
723  # clone the values of the properties for basic types
724  value = type(value)(value)
725  proxy.__set__( newconf, value )
726  except AttributeError:
727  pass
728 
729  for c in self.__children:
730  newconf += c # processes proper copy semantics
731 
732  for n , t in self.__tools.items():
733  newconf.addTool(t, n)
734 
735  for name, value in kwargs.items():
736  setattr(newconf, name, value)
737 
738  return newconf
739 
740  def splitName( self ) :
741  fullname = self.getName()
742  dot = fullname.find('.')
743  if dot != -1 :
744  parentname = fullname[:dot]
745  longname = fullname[dot+1:]
746  else :
747  parentname = ''
748  longname = fullname
749  dot = longname.find('.')
750  if dot != -1 :
751  name = longname[:dot]
752  else :
753  name = longname
754  return parentname, name, longname
755 
756  def addTool( self, tool, name = None ) :
757  if isclass(tool) and issubclass(tool, ConfigurableAlgTool):
758  if name is None:
759  name = tool.__name__
760  priv_tool = tool( self.getName()+ '.' + name )
761  elif isinstance(tool, ConfigurableAlgTool):
762  if name is None:
763  name = tool.splitName()[1]
764  priv_tool = tool.clone( self.getName()+ '.' + name )
765  else:
766  if isclass(tool):
767  classname = tool.__name__
768  else:
769  classname = type(tool).__name__
770  raise TypeError, "addTool requires AlgTool configurable. Got %s type" % classname
771  self.__tools[name] = priv_tool
772  if name in self.__slots__:
773  # this is to avoid that the property hides the tool
774  setattr(self,name,self.__tools[name])
775 
776  def _isInSetDefaults( self ):
777  return self._inSetDefaults
778 
779  def __setupServices( self ):
780  #svcs = self.getServices()
781  #if not svcs:
782  svcs = []
783  #elif type(svcs) == types.StringType:
784  # svcs = [ svcs ]
785 
786  import __main__
787  for svc in svcs:
788  handle = __main__.Service( svc )
789  # services should be configurables as well, but aren't for now
790  # handle.setup()
791 
792  # allow Configurable to make some changes
793  if hasattr( self, 'configure' + svc ):
794  eval( 'self.configure' + svc + '( handle )' )
795 
796  def __setupDlls( self ):
797  dlls = self.getDlls()
798  if not dlls:
799  dlls = []
800  elif type(dlls) == types.StringType:
801  dlls = [ dlls ]
802 
803  from __main__ import theApp
804  dlls = filter( lambda d: d not in theApp.Dlls, dlls )
805  if dlls: theApp.Dlls += dlls
806 
807  def __setupDefaults( self ):
808  # set handle defaults flags to inform __setattr__ that it is being
809  # called during setDefaults of the concrete Configurable
810  self._inSetDefaults = True
811  self.setDefaults( self )
812  self._inSetDefaults = False
813 
814  @staticmethod
815  def _printHeader( indentStr, title ):
816  preLen = Configurable.printHeaderPre
817  postLen = Configurable.printHeaderWidth - preLen - 3 - len(title)# - len(indentStr)
818  postLen = max(preLen,postLen)
819  return indentStr + '/%s %s %s' % (preLen*'*',title,postLen*'*')
820 
821  @staticmethod
822  def _printFooter( indentStr, title ):
823  preLen = Configurable.printHeaderPre
824  postLen = Configurable.printHeaderWidth - preLen - 12 - len(title)# - len(indentStr)
825  postLen = max(preLen,postLen)
826  return indentStr + '\\%s (End of %s) %s' % (preLen*'-',title,postLen*'-')
827 
828  def __repr__( self ):
829  return '<%s at %s>' % (self.getFullJobOptName(),hex(id(self)))
830 
831  def __str__( self, indent = 0, headerLastIndentUnit=indentUnit ):
832  global log # to print some info depending on output level
833  indentStr = indent*Configurable.indentUnit
834  # print header
835  title = self.getPrintTitle()
836  # print line to easily see start-of-configurable
837  if indent > 0:
838  headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
839  else:
840  headerIndent = ''
841  rep = Configurable._printHeader( headerIndent, title )
842  rep += os.linesep
843  # print own properties
844  props = self.getProperties()
845  defs = self.getDefaultProperties()
846  if not props:
847  rep += indentStr + '|-<no properties>' + os.linesep
848  else:
849  # get property name with
850  nameWidth = 0
851  for p in props.keys():
852  nameWidth=max(nameWidth,len(p))
853  for p, v in props.items():
854  # start with indent and property name
855  prefix = indentStr + '|-%-*s' % (nameWidth,p)
856  # add memory address for debugging (not for defaults)
857  if log.isEnabledFor( logging.DEBUG ):
858  if v != Configurable.propertyNoValue:
859  address = ' @%11s' % hex(id(v))
860  else:
861  address = 13*' '
862  prefix += address
863  # add value and default
864  default = defs.get(p)
865  if v == Configurable.propertyNoValue:
866  # show default value as value, and no extra 'default'
867  strVal = repr(default)
868  strDef = None
869  else:
870  # convert configurable to handle
871  if hasattr(v,"getGaudiHandle"):
872  vv = v.getGaudiHandle()
873  else:
874  vv = v
875  if isinstance(vv,GaudiHandle) or isinstance(vv,GaudiHandleArray):
876  strVal = repr(vv)
877  if hasattr(default,"toStringProperty"): # the default may not be a GaudiHandle (?)
878  strDef = repr(default.toStringProperty())
879  else:
880  strDef = repr(default)
881  if strDef == repr(vv.toStringProperty()):
882  strDef = None
883  else:
884  strVal = repr(vv)
885  strDef = repr(default)
886  # add the value
887  line = prefix + ' = ' + strVal
888  # add default if present
889  if strDef is not None:
890  # put default on new line if too big
891  if len(line) + len(strDef) > Configurable.printHeaderWidth:
892  line += os.linesep + indentStr + '| ' + (len(prefix)-len(indentStr)-3)*' '
893  line += ' (default: %s)' % (strDef,)
894  # add the line to the total string
895  rep += line + os.linesep
896  # print out full private configurables
897 ## if isinstance(v,Configurable) and not v.isPublic():
898 ## rep += v.__str__( indent + 1 ) + os.linesep
899 ## elif isinstance(v,GaudiHandleArray):
900 ## for vi in v:
901 ## if isinstance(vi,Configurable) and not vi.isPublic():
902 ## rep += vi.__str__( indent + 1 ) + os.linesep
903 
904  # print configurables + their properties, or loop over sequence
905 ## for cfg in self.__children:
906  for cfg in self.getAllChildren():
907  rep += cfg.__str__( indent + 1, '|=' ) + os.linesep
908 
909  # print line to easily see end-of-configurable. Note: No linesep!
910  rep += Configurable._printFooter( indentStr, title )
911  return rep
912 
913  def isApplicable(self):
914  '''
915  Return True is the instance can be "applied".
916  Always False for plain Configurable instances
917  (i.e. not ConfigurableUser).
918  '''
919  return False
920 
921 ### classes for generic Gaudi component ===========
922 class DummyDescriptor( object ):
923  def __init__( self, name ):
924  self.__name__ = name # conventional
925 
926  def __get__( self, obj, type = None ):
927  return getattr( obj, self.__name__ )
928 
929  def __set__( self, obj, value ):
930  object.__setattr__( obj, self.__name__, value )
931 
933  #__slots__ = { }
934 
935  def __init__( self, name = Configurable.DefaultName ):
936  Configurable.__init__( self, name )
937  self._name = name
938  self._properties = {}
939 
940  def __deepcopy__( self, memo ):
941  return self # algorithms are always shared
942 
943  def getGaudiType( self ): return 'GenericComponent'
944  def getDlls( self ) : pass
945  def getHandle( self ) : pass
946 
947  def __setattr__( self, name, value ):
948  # filter private (user) variables
949  if name[0] == '_':
950  super( ConfigurableGeneric, self ).__setattr__( name, value )
951  return
952 
953  # filter configurable types
954  if isinstance( value, Configurable ):
955  self.__dict__[ name ] = value
956  return
957 
958  # assume all the rest are properties
959  if not name in self._properties:
960  self._properties[ name ] = PropertyProxy( DummyDescriptor( name ) )
961  self._properties[ name ].__set__( self, value )
962 
963  def getJobOptName( self ): return None
964 
965 
966 ### base classes for individual Gaudi algorithms/services/algtools ===========
967 class ConfigurableAlgorithm( Configurable ):
968  __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
969  'Enable' : 1, 'ErrorMax' : 1, 'ErrorCount' : 0, 'AuditAlgorithms' : 0, \
970  'AuditInitialize' : 0, 'AuditReinitialize' : 0, 'AuditExecute' : 0, \
971  'AuditFinalize' : 0, 'AuditBeginRun' : 0, 'AuditEndRun' : 0 }
972 
973  def __init__( self, name = Configurable.DefaultName ):
974  super( ConfigurableAlgorithm, self ).__init__( name )
975  name = self.getName()
976  self._jobOptName = name[ name.find('/')+1 : ] # strips class
977 
978  def __deepcopy__( self, memo ):
979  return self # algorithms are always shared
980 
981  def getHandle( self ):
982  return iAlgorithm( self.getJobOptName() )
983 
984  def getGaudiType( self ):
985  return 'Algorithm'
986 
987  def getJobOptName( self ):
988  return self._jobOptName
989 
990 
992  __slots__ = { 'OutputLevel' : 0, \
993  'AuditServices' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
994 
995  def __deepcopy__( self, memo ):
996  return self # services are always shared
997 
998  def copyChild( self, child ):
999  return child # full sharing
1000 
1001  def getHandle( self ):
1002  return iService( self._name )
1003 
1004  def getGaudiType( self ):
1005  return 'Service'
1006 
1007  def getGaudiHandle( self ):
1008  return ServiceHandle( self.toStringProperty() )
1009 
1010  def toStringProperty( self ):
1011  # called on conversion to a string property for the jocat
1012  return self.getName()
1013 
1014 
1016  __slots__ = { '_jobOptName' : '', 'OutputLevel' : 0, \
1017  'AuditTools' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
1018 
1019  def __init__( self, name = Configurable.DefaultName ):
1020  super( ConfigurableAlgTool, self ).__init__( name )
1021  if '.' not in self._name:
1022  # Public tools must have ToolSvc as parent
1023  self._name = "ToolSvc." + self._name
1024  name = self.getName()
1025  name = name[ name.find('/')+1 : ] # strips class, if any
1026  self._jobOptName = name
1027 
1028  def getHandle( self ):
1029  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1030  return iProperty( self.getJobOptName() )
1031 
1032  def getGaudiType( self ):
1033  return 'AlgTool'
1034 
1035  def getGaudiHandle( self ):
1036  if self.isPublic():
1037  return PublicToolHandle( self.toStringProperty() )
1038  else:
1039  return PrivateToolHandle( self.toStringProperty() )
1040 
1041  def getPrintTitle(self):
1042  if self.isPublic():
1043  pop = 'Public '
1044  else:
1045  pop = 'Private '
1046  return pop + Configurable.getPrintTitle(self)
1047 
1048  def setParent( self, parentName ):
1049 # print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
1050 # print "Calling stack:"
1051 # import traceback
1052 # traceback.print_stack()
1053  # propagate parent to AlgTools in children
1054  for c in self.getAllChildren():
1055  if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
1056 
1057  # update my own parent
1058  name = self.getName()
1059  name = name[name.rfind('.')+1:] # Name of the instance
1060  self._jobOptName = self._name = parentName + '.' + name
1061 
1062  def getParent( self ):
1063  dot = self._jobOptName.rfind('.')
1064  if dot != -1:
1065  return self._jobOptName[:dot]
1066  else:
1067  return ""
1068 
1069  def hasParent( self, parent ):
1070  return self._jobOptName.startswith( parent + '.' )
1071 
1072  def getJobOptName( self ):
1073  return self._jobOptName
1074 
1075  def isPublic( self ):
1076  return self.isInToolSvc()
1077 
1078  def isInToolSvc( self ):
1079  return self._jobOptName.startswith('ToolSvc.')
1080 
1081  def toStringProperty( self ):
1082  # called on conversion to a string property for the jocat
1083  return self.getFullName()
1084 
1085  def getFullName( self ) :
1086  # for Tools, the "full name" means "Type/LocalName",
1087  # without the names of the parents
1088  name = self.getName()
1089  # strip off everything before the last '.'
1090  name = name[name.rfind('.')+1:]
1091  return str( self.getType() + '/' + name )
1092 
1093 
1094 ### FIXME: this is just a placeholder, waiting for a real implementation
1095 ### It is sufficient to get us going... (and import a PkgConf which
1096 ### happens to contain an Auditor...)
1098  __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
1099  'Enable' : 1 }
1100 
1101  def __init__( self, name = Configurable.DefaultName ):
1102  super( ConfigurableAuditor, self ).__init__( name )
1103  name = self.getName()
1104  name = name[ name.find('/')+1 : ] # strips class, if any
1105  self._jobOptName = name
1106 
1107  def getHandle( self ):
1108  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1109  return iProperty( self.getJobOptName() )
1110 
1111  def getGaudiType( self ):
1112  return 'Auditor'
1113 
1114  def getJobOptName( self ):
1115  return self._jobOptName
1116 
1117  def toStringProperty( self ):
1118  # called on conversion to a string property for the jocat
1119  return self.getType() + '/' + self.getName()
1120 
1122  __slots__ = { "__users__": [],
1123  "__used_instances__": [],
1124  "_enabled": True,
1125  "_applied": False }
1126  ## list of ConfigurableUser classes this one is going to modify in the
1127  # __apply_configuration__ method.
1128  # The list may contain class objects, strings representing class objects or
1129  # tuples with the class object (or a string) as first element and the instance
1130  # name as second element.
1131  # If the instance name is None or not present, the function _instanceName()
1132  # is used to determine the name of the instance (the default implementation
1133  # returns "<this name>_<other name>".
1134  __used_configurables__ = []
1135  ## list of ConfigurableUser classes this one is going to query in the
1136  # __apply_configuration__ method
1137  __queried_configurables__ = []
1138  def __init__( self, name = Configurable.DefaultName, _enabled = True, **kwargs ):
1139  super( ConfigurableUser, self ).__init__( name )
1140  for n, v in kwargs.items():
1141  setattr(self, n, v)
1142  self._enabled = _enabled
1143  self.__users__ = []
1144  self._applied = False
1145 
1146  # Needed to retrieve the actual class if the declaration in __used_configurables__
1147  # and __queried_configurables__ is done with strings.
1148  from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
1149 
1150  # Set the list of users of the used configurables
1151  #
1153  for used in self.__used_configurables__:
1154  # By default we want to use the default name of the instances
1155  # for the used configurables
1156  used_name = Configurable.DefaultName
1157  # If the entry in the list is a tuple, we need a named instance
1158  if type(used) is tuple:
1159  used, used_name = used # we re-set used to re-use the code below
1160  if not used_name:
1161  used_name = self._instanceName(used)
1162  # Check is 'used' is a string or not
1163  if type(used) is str:
1164  used_class = confDbGetConfigurable(used)
1165  else:
1166  used_class = used
1167  # Instantiate the configurable that we are going to use
1168  try:
1169  inst = used_class(name = used_name, _enabled = False)
1170  except AttributeError:
1171  # This cover the case where the used configurable is not a
1172  # ConfigurableUser instance, i.e. id doesn't have the attribute
1173  # '_enabled'.
1174  inst = used_class(name = used_name)
1175  self.__addActiveUseOf(inst)
1176  for queried in self.__queried_configurables__:
1177  try:
1178  if type(queried) is str:
1179  queried = confDbGetConfigurable(used)
1180  inst = queried(_enabled = False)
1181  except AttributeError:
1182  inst = queried()
1183  self.__addPassiveUseOf(inst)
1184  def __addActiveUseOf(self, other):
1185  """
1186  Declare that we are going to modify the Configurable 'other' in our
1187  __apply_configuration__.
1188  """
1189  self.__used_instances__.append(other)
1190  if hasattr(other, "__users__"): # allow usage of plain Configurables
1191  other.__users__.append(self)
1192  def __addPassiveUseOf(self, other):
1193  """
1194  Declare that we are going to retrieve property values from the
1195  ConfigurableUser 'other' in our __apply_configuration__.
1196  """
1197  if not isinstance(other, ConfigurableUser):
1198  raise Error("'%s': Cannot make passive use of '%s', it is not a ConfigurableUser" % (self.name(), other.name()))
1199  other.__addActiveUseOf(self)
1200  def getGaudiType( self ):
1201  return 'User'
1202  def getDlls( self ):
1203  return None
1204  def getHandle( self ):
1205  return None
1206 
1207  def __detach_used__(self):
1208  """
1209  Remove this ConfigurableUser instance from the users list of the used
1210  instances.
1211  """
1212  for used in self.__used_instances__:
1213  if hasattr(used, "__users__"): # allow usage of plain Configurables
1214  used.__users__.remove(self)
1215 
1216  def propagateProperty(self, name, others = None, force = True):
1217  """
1218  Propagate the property 'name' (if set) to other configurables (if possible).
1219  'others' can be:
1220  None:
1221  propagate to all the entries in __used_configurables__
1222  a configurable instance:
1223  propagate only to it
1224  list of configurable instances:
1225  propagate to all of them.
1226 
1227 
1228  The logic is:
1229  - if the local property is set, the other property will be overwritten
1230  - local property not set and other set => keep other
1231  - local property not set and other not set => overwrite the default for
1232  ConfigurableUser instances and set the property for Configurables
1233  """
1234  # transform 'others' to a list of configurable instances
1235  if others is None:
1236  others = self.__used_instances__
1237  elif type(others) not in [ list, tuple ] :
1238  others = [ others ]
1239  # these can be computed before the loop
1240  local_is_set = self.isPropertySet(name)
1241  value = self.getProp(name)
1242  # loop over the others that do have 'name' in their slots
1243  for other in [ o for o in others if name in o.__slots__ ]:
1244  # If self property is set, use it
1245  if local_is_set:
1246  if other.isPropertySet(name):
1247  log.warning("Property '%(prop)s' is set in both '%(self)s' and '%(other)s', using '%(self)s.%(prop)s'"%
1248  { "self": self.name(),
1249  "other": other.name(),
1250  "prop": name } )
1251  other.setProp(name, value)
1252  # If not, and other property also not set, propagate the default
1253  elif not other.isPropertySet(name):
1254  if isinstance(other,ConfigurableUser):
1255  otherType = type(other._properties[name].getDefault())
1256  other._properties[name].setDefault(value)
1257  if otherType in [list, dict]:
1258  # Special case for list and dictionaries:
1259  # also set the property to the same value of the default (copy)
1260  other.setProp(name, otherType(value))
1261  else:
1262  other.setProp(name, value)
1263  # If not set and other set, do nothing
1264 
1265  def propagateProperties(self, names = None, others = None, force = True):
1266  """
1267  Call propagateProperty for each property listed in 'names'.
1268  If 'names' is None, all the properties are propagated.
1269  """
1270  if names is None:
1271  # use all the non-private slots
1272  names = [ p for p in self.__slots__ if not p.startswith("_") ]
1273  for n in names:
1274  self.propagateProperty(n, others, force)
1275 
1277  """
1278  Function to be overridden to convert the high level configuration into a
1279  low level one.
1280  The default implementation calls applyConf, which is the method defined
1281  in some ConfigurableUser implementations.
1282  """
1283  return self.applyConf()
1284 
1285  def applyConf( self ):
1286  """
1287  Function to be overridden to convert the high level configuration into a
1288  low level one.
1289  """
1290  pass
1291 
1292  def _instanceName(self, cls):
1293  """
1294  Function used to define the name of the private instance of a given class
1295  name.
1296  This method is used when the __used_configurables_property__ declares the
1297  need of a private used configurable without specifying the name.
1298  """
1299  if type(cls) is str:
1300  clName = cls
1301  else:
1302  clName = cls.__name__
1303  return "%s_%s" % (self.name(), clName)
1304 
1305  def getUsedInstance(self, name):
1306  """
1307  Return the used instance with a given name.
1308  """
1309  for i in self.__used_instances__:
1310  if i.name() == name:
1311  if hasattr(i, "_enabled"):
1312  # ensure that the instances retrieved through the method are
1313  # enabled
1314  i._enabled = True
1315  return i
1316  raise KeyError(name)
1317 
1318  def isApplicable(self):
1319  '''
1320  Return True is the instance can be "applied".
1321  '''
1322  return (not self.__users__) and (not self._applied)
1323 
1324 
1325 # list of callables to be called after all the __apply_configuration__ are called.
1326 postConfigActions = []
1328  """
1329  Add a new callable ('function') to the list of post-configuration actions.
1330  If the callable is already in the list, it is moved to the end of the list.
1331  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1332  """
1333  try:
1334  postConfigActions.remove(function)
1335  except:
1336  pass
1337  postConfigActions.append(function)
1339  """
1340  Remove a callable from the list of post-config actions.
1341  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1342  """
1343  postConfigActions.remove(function)
1344 
1345 _appliedConfigurableUsers_ = False
1347  """
1348  Call the apply method of all the ConfigurableUser instances respecting the
1349  dependencies. First the C.U.s that are not used by anybody, then the used
1350  ones, when they are not used anymore.
1351  """
1352  # Avoid double calls
1353  global _appliedConfigurableUsers_, postConfigActions
1354  if _appliedConfigurableUsers_:
1355  return
1356  _appliedConfigurableUsers_ = True
1357 
1358  def applicableConfUsers():
1359  '''
1360  Generator returning all the configurables that can be applied in the
1361  order in which they can be applied.
1362  '''
1363  # This is tricky...
1364  # We keep on looking for the first configurable that can be applied.
1365  # When we cannot find any, 'next()' raises a StopIteration that is
1366  # propagated outside of the infinite loop and the function, then handled
1367  # correctly from outside (it is the exception that is raised when you
1368  # exit from a generator).
1369  # Checking every time the full list is inefficient, but it is the
1370  # easiest way to fix bug #103803.
1371  # <https://savannah.cern.ch/bugs/?103803>
1372  while True:
1373  yield (c for c in Configurable.allConfigurables.values()
1374  if c.isApplicable()).next()
1375 
1376  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1377  for c in applicableConfUsers():
1378  if c._enabled:
1379  log.info("applying configuration of %s", c.name())
1380  if debugApplyOrder:
1381  sys.stderr.write('applying %r' % c)
1382  c.__apply_configuration__()
1383  log.info(c)
1384  else:
1385  log.info("skipping configuration of %s", c.name())
1386  c._applied = True # flag the instance as already applied
1387  if hasattr(c, "__detach_used__"):
1388  # tells the used configurables that they are not needed anymore
1389  c.__detach_used__()
1390 
1391  # check for dependency loops
1392  leftConfUsers = [c for c in Configurable.allConfigurables.values()
1393  if hasattr(c, '__apply_configuration__') and
1394  c._enabled and not c._applied]
1395  # if an enabled configurable has not been applied, there must be a dependency loop
1396  if leftConfUsers:
1397  raise Error("Detected loop in the ConfigurableUser"
1398  " dependencies: %r" % [ c.name()
1399  for c in leftConfUsers ])
1400  # ensure that all the Handles have been triggered
1401  known = set()
1402  unknown = set(Configurable.allConfigurables)
1403  while unknown:
1404  for k in unknown:
1405  if not known: # do not print during the first iteration
1406  log.debug('new configurable created automatically: %s', k)
1407  # this trigger the instantiation from handles
1408  Configurable.allConfigurables[k].properties()
1409  known.add(k)
1410  unknown -= known
1411  # Call post-config actions
1412  for action in postConfigActions:
1413  action()
1414 
1416  """
1417  Obsolete (buggy) implementation of applyConfigurableUsers(), kept to provide
1418  backward compatibility for configurations that where relying (implicitly) on
1419  bug #103803, or on a specific (non guaranteed) order of execution.
1420 
1421  @see applyConfigurableUsers()
1422  """
1423  # Avoid double calls
1424  global _appliedConfigurableUsers_, postConfigActions
1425  if _appliedConfigurableUsers_:
1426  return
1427  _appliedConfigurableUsers_ = True
1428 
1429  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1430  confUsers = [ c
1431  for c in Configurable.allConfigurables.values()
1432  if hasattr(c,"__apply_configuration__") ]
1433  applied = True # needed to detect dependency loops
1434  while applied and confUsers:
1435  newConfUsers = [] # list of conf users that cannot be applied yet
1436  applied = False
1437  for c in confUsers:
1438  if hasattr(c,"__users__") and c.__users__:
1439  newConfUsers.append(c) # cannot use this one yet
1440  else: # it does not have users or the list is empty
1441  applied = True
1442  # the ConfigurableUser is enabled if it doesn't have an _enabled
1443  # property or its value is True
1444  enabled = (not hasattr(c, "_enabled")) or c._enabled
1445  if enabled:
1446  log.info("applying configuration of %s", c.name())
1447  if debugApplyOrder:
1448  sys.stderr.write('applying %r' % c)
1449  c.__apply_configuration__()
1450  log.info(c)
1451  else:
1452  log.info("skipping configuration of %s", c.name())
1453  if hasattr(c, "__detach_used__"):
1454  # tells the used configurables that they are not needed anymore
1455  c.__detach_used__()
1456  confUsers = newConfUsers # list of C.U.s still to go
1457  if confUsers:
1458  # this means that some C.U.s could not be applied because of a dependency loop
1459  raise Error("Detected loop in the ConfigurableUser "
1460  " dependencies: %r" % [ c.name()
1461  for c in confUsers ])
1462  # ensure that all the Handles have been triggered
1463  known = set()
1464  unknown = set(Configurable.allConfigurables)
1465  while unknown:
1466  for k in unknown:
1467  if not known: # do not print during the first iteration
1468  log.debug('new configurable created automatically: %s', k)
1469  # this trigger the instantiation from handles
1470  Configurable.allConfigurables[k].properties()
1471  known.add(k)
1472  unknown -= known
1473  # Call post-config actions
1474  for action in postConfigActions:
1475  action()
1476 
1478  """
1479  Function to select all and only the configurables that have to be used in
1480  GaudiPython.AppMgr constructor.
1481  This is needed because in Athena the implementation have to be different (the
1482  configuration is used in a different moment).
1483  """
1484  return [ k
1485  for k, v in Configurable.allConfigurables.items()
1486  if v.getGaudiType() != "User" ] # Exclude ConfigurableUser instances
1487 
1488 def purge():
1489  """
1490  Clean up all configurations and configurables.
1491  """
1492  for c in Configurable.allConfigurables.values():
1493  c.__class__.configurables.clear()
1494  Configurable.allConfigurables.clear()
1495  # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
1496  # migrated to the correct class when this is known.
1497  ConfigurableGeneric.configurables.clear()
1498  from ProcessJobOptions import _included_files
1499  import os.path, sys
1500  for file in _included_files:
1501  dirname, basname = os.path.split(file)
1502  basname, ext = os.path.splitext(basname)
1503  if basname in sys.modules:
1504  del sys.modules[basname]
1505  _included_files.clear()
string action
Definition: merge_files.py:89
list __queried_configurables__
list of ConfigurableUser classes this one is going to query in the apply_configuration method ...
list __used_configurables__
list of ConfigurableUser classes this one is going to modify in the apply_configuration method...
string type
Definition: gaudirun.py:126
this metaclass installs PropertyProxy descriptors for Gaudi properties
iAlgorithm
The basic module.
Definition: GaudiAlgs.py:56
def isApplicable
if isinstance(v,Configurable) and not v.isPublic(): rep += v.__str__( indent + 1 ) + os...
classes for generic Gaudi component ===========