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 
573  """Get all properties with their description string as { name : (value, desc) }."""
574  props = {}
575  for name, proxy in self._properties.items():
576  try:
577  props[ name ] = ( proxy.__get__( self ), proxy.__doc__)
578  except AttributeError:
579  props[ name ] = ( Configurable.propertyNoValue, proxy.__doc__)
580  return props
581 
582  def getValuedProperties( self ):
583  props = {}
584  for name, proxy in self._properties.items():
585  if self.isPropertySet(name):
586  value = proxy.__get__( self )
587  if hasattr(value, 'getFullName') :
588  value = value.getFullName()
589  elif type(value) in [list, tuple]:
590  new_value = []
591  for i in value:
592  if hasattr(i, 'getFullName'):
593  new_value.append(i.getFullName())
594  else:
595  new_value.append(i)
596  value = type(value)(new_value)
597  elif type(value) is dict:
598  new_value = {}
599  for i in value:
600  if hasattr(value[i], 'getFullName'):
601  new_value[i] = value[i].getFullName()
602  else:
603  new_value[i] = value[i]
604  value = new_value
605  props[ name ] = value
606 
607  return props
608 
609  def properties( self ):
610  return self.getProperties() # compatibility
611 
612  @classmethod
614  class collector:
615  pass
616 
617  # user provided defaults
618  c = collector()
619  cls.setDefaults( c )
620 
621  # defaults from C++
622  for k,v in cls._properties.items():
623  if not k in c.__dict__ and hasattr( v, 'default' ):
624  c.__dict__[ k ] = v.default
625 
626  return c.__dict__
627 
628  @classmethod
629  def getDefaultProperty( cls, name ):
630  class collector:
631  pass
632 
633  # user provided defaults
634  c = collector()
635  cls.setDefaults( c )
636 
637  if name in c.__dict__:
638  return c.__dict__[ name ]
639 
640  # defaults from C++
641  try:
642  v = cls._properties[name]
643  if hasattr( v, 'default' ):
644  return v.default
645  except KeyError:
646  pass
647 
648  return None
649 
650  def getProp(self, name):
651  """Returns the value of the given property.
652  """
653  if hasattr(self, name):
654  return getattr(self, name)
655  else:
656  return self.getDefaultProperties()[name]
657 
658  def setProp(self, name, value):
659  """Set the value of a given property
660  """
661  return setattr(self, name, value)
662 
663  def isPropertySet(self, name):
664  """Tell if the property 'name' has been set or not.
665 
666  Because of a problem with list and dictionary properties, in those cases
667  if the value is equal to the default, the property is considered as not
668  set.
669  """
670  if not hasattr(self, name):
671  return False
672  else:
673  try:
674  default = self.getDefaultProperties()[name]
675  if isinstance(default, (list, dict)):
676  value = getattr(self, name)
677  return value != default
678  except KeyError:
679  pass # no default found
680  return True
681 
682  def getType( cls ):
683  return cls.__name__
684 
685  def getName( self ):
686  return self._name
687 
688  def name( self ):
689  return self.getName()
690 
691  def getJobOptName( self ): # full hierachical name
692  return self.getName()
693 
694  def isPublic( self ):
695  return True
696 
697  # for a couple of existing uses out there
698  def jobOptName( self ):
699  log.error( "jobOptName() is deprecated, use getJobOptName() instead for consistency%s",
700  error_explanation )
701  return self.getJobOptName() # compatibility
702 
703  def getFullName( self ) :
704  return str( self.getType() + '/' + self.getName() )
705 
706  def getFullJobOptName( self ):
707  return "%s/%s" % (self.getType(),self.getJobOptName() or self.getName())
708 
709  def getPrintTitle(self):
710  return self.getGaudiType() + ' ' + self.getTitleName()
711 
712  def getTitleName( self ):
713  if log.isEnabledFor( logging.DEBUG ):
714  return self.getFullJobOptName()
715  else:
716  return self.getFullName()
717 
718  def setDefaults( cls, handle ):
719  pass
720 
721  def clone( self, name = None, **kwargs ) :
722  if not name :
723  if hasattr(self, 'DefaultedName' ) : name = self.DefaultedName
724  else : name = self.getType()
725 
726  newconf = Configurable.__new__( self.__class__, name )
727  self.__class__.__init__( newconf, name )
728 
729  for proxy in self._properties.values():
730  try :
731  value = proxy.__get__( self )
732  if type(value) in [ str, list, dict, tuple ]:
733  # clone the values of the properties for basic types
734  value = type(value)(value)
735  proxy.__set__( newconf, value )
736  except AttributeError:
737  pass
738 
739  for c in self.__children:
740  newconf += c # processes proper copy semantics
741 
742  for n , t in self.__tools.items():
743  newconf.addTool(t, n)
744 
745  for name, value in kwargs.items():
746  setattr(newconf, name, value)
747 
748  return newconf
749 
750  def splitName( self ) :
751  fullname = self.getName()
752  dot = fullname.find('.')
753  if dot != -1 :
754  parentname = fullname[:dot]
755  longname = fullname[dot+1:]
756  else :
757  parentname = ''
758  longname = fullname
759  dot = longname.find('.')
760  if dot != -1 :
761  name = longname[:dot]
762  else :
763  name = longname
764  return parentname, name, longname
765 
766  def addTool( self, tool, name = None ) :
767  if isclass(tool) and issubclass(tool, ConfigurableAlgTool):
768  if name is None:
769  name = tool.__name__
770  priv_tool = tool( self.getName()+ '.' + name )
771  elif isinstance(tool, ConfigurableAlgTool):
772  if name is None:
773  name = tool.splitName()[1]
774  priv_tool = tool.clone( self.getName()+ '.' + name )
775  else:
776  if isclass(tool):
777  classname = tool.__name__
778  else:
779  classname = type(tool).__name__
780  raise TypeError, "addTool requires AlgTool configurable. Got %s type" % classname
781  self.__tools[name] = priv_tool
782  if name in self.__slots__:
783  # this is to avoid that the property hides the tool
784  setattr(self,name,self.__tools[name])
785 
786  def _isInSetDefaults( self ):
787  return self._inSetDefaults
788 
789  def __setupServices( self ):
790  #svcs = self.getServices()
791  #if not svcs:
792  svcs = []
793  #elif type(svcs) == types.StringType:
794  # svcs = [ svcs ]
795 
796  import __main__
797  for svc in svcs:
798  handle = __main__.Service( svc )
799  # services should be configurables as well, but aren't for now
800  # handle.setup()
801 
802  # allow Configurable to make some changes
803  if hasattr( self, 'configure' + svc ):
804  eval( 'self.configure' + svc + '( handle )' )
805 
806  def __setupDlls( self ):
807  dlls = self.getDlls()
808  if not dlls:
809  dlls = []
810  elif type(dlls) == types.StringType:
811  dlls = [ dlls ]
812 
813  from __main__ import theApp
814  dlls = filter( lambda d: d not in theApp.Dlls, dlls )
815  if dlls: theApp.Dlls += dlls
816 
817  def __setupDefaults( self ):
818  # set handle defaults flags to inform __setattr__ that it is being
819  # called during setDefaults of the concrete Configurable
820  self._inSetDefaults = True
821  self.setDefaults( self )
822  self._inSetDefaults = False
823 
824  @staticmethod
825  def _printHeader( indentStr, title ):
826  preLen = Configurable.printHeaderPre
827  postLen = Configurable.printHeaderWidth - preLen - 3 - len(title)# - len(indentStr)
828  postLen = max(preLen,postLen)
829  return indentStr + '/%s %s %s' % (preLen*'*',title,postLen*'*')
830 
831  @staticmethod
832  def _printFooter( indentStr, title ):
833  preLen = Configurable.printHeaderPre
834  postLen = Configurable.printHeaderWidth - preLen - 12 - len(title)# - len(indentStr)
835  postLen = max(preLen,postLen)
836  return indentStr + '\\%s (End of %s) %s' % (preLen*'-',title,postLen*'-')
837 
838  def __repr__( self ):
839  return '<%s at %s>' % (self.getFullJobOptName(),hex(id(self)))
840 
841  def __str__( self, indent = 0, headerLastIndentUnit=indentUnit ):
842  global log # to print some info depending on output level
843  indentStr = indent*Configurable.indentUnit
844  # print header
845  title = self.getPrintTitle()
846  # print line to easily see start-of-configurable
847  if indent > 0:
848  headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
849  else:
850  headerIndent = ''
851  rep = Configurable._printHeader( headerIndent, title )
852  rep += os.linesep
853  # print own properties
854  props = self.getProperties()
855  defs = self.getDefaultProperties()
856  if not props:
857  rep += indentStr + '|-<no properties>' + os.linesep
858  else:
859  # get property name with
860  nameWidth = 0
861  for p in props.keys():
862  nameWidth=max(nameWidth,len(p))
863  for p, v in props.items():
864  # start with indent and property name
865  prefix = indentStr + '|-%-*s' % (nameWidth,p)
866  # add memory address for debugging (not for defaults)
867  if log.isEnabledFor( logging.DEBUG ):
868  if v != Configurable.propertyNoValue:
869  address = ' @%11s' % hex(id(v))
870  else:
871  address = 13*' '
872  prefix += address
873  # add value and default
874  default = defs.get(p)
875  if v == Configurable.propertyNoValue:
876  # show default value as value, and no extra 'default'
877  strVal = repr(default)
878  strDef = None
879  else:
880  # convert configurable to handle
881  if hasattr(v,"getGaudiHandle"):
882  vv = v.getGaudiHandle()
883  else:
884  vv = v
885  if isinstance(vv,GaudiHandle) or isinstance(vv,GaudiHandleArray):
886  strVal = repr(vv)
887  if hasattr(default,"toStringProperty"): # the default may not be a GaudiHandle (?)
888  strDef = repr(default.toStringProperty())
889  else:
890  strDef = repr(default)
891  if strDef == repr(vv.toStringProperty()):
892  strDef = None
893  else:
894  strVal = repr(vv)
895  strDef = repr(default)
896  # add the value
897  line = prefix + ' = ' + strVal
898  # add default if present
899  if strDef is not None:
900  # put default on new line if too big
901  if len(line) + len(strDef) > Configurable.printHeaderWidth:
902  line += os.linesep + indentStr + '| ' + (len(prefix)-len(indentStr)-3)*' '
903  line += ' (default: %s)' % (strDef,)
904  # add the line to the total string
905  rep += line + os.linesep
906  # print out full private configurables
907 ## if isinstance(v,Configurable) and not v.isPublic():
908 ## rep += v.__str__( indent + 1 ) + os.linesep
909 ## elif isinstance(v,GaudiHandleArray):
910 ## for vi in v:
911 ## if isinstance(vi,Configurable) and not vi.isPublic():
912 ## rep += vi.__str__( indent + 1 ) + os.linesep
913 
914  # print configurables + their properties, or loop over sequence
915 ## for cfg in self.__children:
916  for cfg in self.getAllChildren():
917  rep += cfg.__str__( indent + 1, '|=' ) + os.linesep
918 
919  # print line to easily see end-of-configurable. Note: No linesep!
920  rep += Configurable._printFooter( indentStr, title )
921  return rep
922 
923  def isApplicable(self):
924  '''
925  Return True is the instance can be "applied".
926  Always False for plain Configurable instances
927  (i.e. not ConfigurableUser).
928  '''
929  return False
930 
931 ### classes for generic Gaudi component ===========
932 class DummyDescriptor( object ):
933  def __init__( self, name ):
934  self.__name__ = name # conventional
935 
936  def __get__( self, obj, type = None ):
937  return getattr( obj, self.__name__ )
938 
939  def __set__( self, obj, value ):
940  object.__setattr__( obj, self.__name__, value )
941 
943  #__slots__ = { }
944 
945  def __init__( self, name = Configurable.DefaultName ):
946  Configurable.__init__( self, name )
947  self._name = name
948  self._properties = {}
949 
950  def __deepcopy__( self, memo ):
951  return self # algorithms are always shared
952 
953  def getGaudiType( self ): return 'GenericComponent'
954  def getDlls( self ) : pass
955  def getHandle( self ) : pass
956 
957  def __setattr__( self, name, value ):
958  # filter private (user) variables
959  if name[0] == '_':
960  super( ConfigurableGeneric, self ).__setattr__( name, value )
961  return
962 
963  # filter configurable types
964  if isinstance( value, Configurable ):
965  self.__dict__[ name ] = value
966  return
967 
968  # assume all the rest are properties
969  if not name in self._properties:
970  self._properties[ name ] = PropertyProxy( DummyDescriptor( name ) )
971  self._properties[ name ].__set__( self, value )
972 
973  def getJobOptName( self ): return None
974 
975 
976 ### base classes for individual Gaudi algorithms/services/algtools ===========
977 class ConfigurableAlgorithm( Configurable ):
978  __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
979  'Enable' : 1, 'ErrorMax' : 1, 'ErrorCount' : 0, 'AuditAlgorithms' : 0, \
980  'AuditInitialize' : 0, 'AuditReinitialize' : 0, 'AuditExecute' : 0, \
981  'AuditFinalize' : 0, 'AuditBeginRun' : 0, 'AuditEndRun' : 0 }
982 
983  def __init__( self, name = Configurable.DefaultName ):
984  super( ConfigurableAlgorithm, self ).__init__( name )
985  name = self.getName()
986  self._jobOptName = name[ name.find('/')+1 : ] # strips class
987 
988  def __deepcopy__( self, memo ):
989  return self # algorithms are always shared
990 
991  def getHandle( self ):
992  return iAlgorithm( self.getJobOptName() )
993 
994  def getGaudiType( self ):
995  return 'Algorithm'
996 
997  def getJobOptName( self ):
998  return self._jobOptName
999 
1000 
1002  __slots__ = { 'OutputLevel' : 0, \
1003  'AuditServices' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
1004 
1005  def __deepcopy__( self, memo ):
1006  return self # services are always shared
1007 
1008  def copyChild( self, child ):
1009  return child # full sharing
1010 
1011  def getHandle( self ):
1012  return iService( self._name )
1013 
1014  def getGaudiType( self ):
1015  return 'Service'
1016 
1017  def getGaudiHandle( self ):
1018  return ServiceHandle( self.toStringProperty() )
1019 
1020  def toStringProperty( self ):
1021  # called on conversion to a string property for the jocat
1022  return self.getName()
1023 
1024 
1026  __slots__ = { '_jobOptName' : '', 'OutputLevel' : 0, \
1027  'AuditTools' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
1028 
1029  def __init__( self, name = Configurable.DefaultName ):
1030  super( ConfigurableAlgTool, self ).__init__( name )
1031  if '.' not in self._name:
1032  # Public tools must have ToolSvc as parent
1033  self._name = "ToolSvc." + self._name
1034  name = self.getName()
1035  name = name[ name.find('/')+1 : ] # strips class, if any
1036  self._jobOptName = name
1037 
1038  def getHandle( self ):
1039  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1040  return iProperty( self.getJobOptName() )
1041 
1042  def getGaudiType( self ):
1043  return 'AlgTool'
1044 
1045  def getGaudiHandle( self ):
1046  if self.isPublic():
1047  return PublicToolHandle( self.toStringProperty() )
1048  else:
1049  return PrivateToolHandle( self.toStringProperty() )
1050 
1051  def getPrintTitle(self):
1052  if self.isPublic():
1053  pop = 'Public '
1054  else:
1055  pop = 'Private '
1056  return pop + Configurable.getPrintTitle(self)
1057 
1058  def setParent( self, parentName ):
1059 # print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
1060 # print "Calling stack:"
1061 # import traceback
1062 # traceback.print_stack()
1063  # propagate parent to AlgTools in children
1064  for c in self.getAllChildren():
1065  if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
1066 
1067  # update my own parent
1068  name = self.getName()
1069  name = name[name.rfind('.')+1:] # Name of the instance
1070  self._jobOptName = self._name = parentName + '.' + name
1071 
1072  def getParent( self ):
1073  dot = self._jobOptName.rfind('.')
1074  if dot != -1:
1075  return self._jobOptName[:dot]
1076  else:
1077  return ""
1078 
1079  def hasParent( self, parent ):
1080  return self._jobOptName.startswith( parent + '.' )
1081 
1082  def getJobOptName( self ):
1083  return self._jobOptName
1084 
1085  def isPublic( self ):
1086  return self.isInToolSvc()
1087 
1088  def isInToolSvc( self ):
1089  return self._jobOptName.startswith('ToolSvc.')
1090 
1091  def toStringProperty( self ):
1092  # called on conversion to a string property for the jocat
1093  return self.getFullName()
1094 
1095  def getFullName( self ) :
1096  # for Tools, the "full name" means "Type/LocalName",
1097  # without the names of the parents
1098  name = self.getName()
1099  # strip off everything before the last '.'
1100  name = name[name.rfind('.')+1:]
1101  return str( self.getType() + '/' + name )
1102 
1103 
1104 ### FIXME: this is just a placeholder, waiting for a real implementation
1105 ### It is sufficient to get us going... (and import a PkgConf which
1106 ### happens to contain an Auditor...)
1108  __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
1109  'Enable' : 1 }
1110 
1111  def __init__( self, name = Configurable.DefaultName ):
1112  super( ConfigurableAuditor, self ).__init__( name )
1113  name = self.getName()
1114  name = name[ name.find('/')+1 : ] # strips class, if any
1115  self._jobOptName = name
1116 
1117  def getHandle( self ):
1118  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1119  return iProperty( self.getJobOptName() )
1120 
1121  def getGaudiType( self ):
1122  return 'Auditor'
1123 
1124  def getJobOptName( self ):
1125  return self._jobOptName
1126 
1127  def toStringProperty( self ):
1128  # called on conversion to a string property for the jocat
1129  return self.getType() + '/' + self.getName()
1130 
1132  __slots__ = { "__users__": [],
1133  "__used_instances__": [],
1134  "_enabled": True,
1135  "_applied": False }
1136  ## list of ConfigurableUser classes this one is going to modify in the
1137  # __apply_configuration__ method.
1138  # The list may contain class objects, strings representing class objects or
1139  # tuples with the class object (or a string) as first element and the instance
1140  # name as second element.
1141  # If the instance name is None or not present, the function _instanceName()
1142  # is used to determine the name of the instance (the default implementation
1143  # returns "<this name>_<other name>".
1144  __used_configurables__ = []
1145  ## list of ConfigurableUser classes this one is going to query in the
1146  # __apply_configuration__ method
1147  __queried_configurables__ = []
1148  def __init__( self, name = Configurable.DefaultName, _enabled = True, **kwargs ):
1149  super( ConfigurableUser, self ).__init__( name )
1150  for n, v in kwargs.items():
1151  setattr(self, n, v)
1152  self._enabled = _enabled
1153  self.__users__ = []
1154  self._applied = False
1155 
1156  # Needed to retrieve the actual class if the declaration in __used_configurables__
1157  # and __queried_configurables__ is done with strings.
1158  from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
1159 
1160  # Set the list of users of the used configurables
1161  #
1163  for used in self.__used_configurables__:
1164  # By default we want to use the default name of the instances
1165  # for the used configurables
1166  used_name = Configurable.DefaultName
1167  # If the entry in the list is a tuple, we need a named instance
1168  if type(used) is tuple:
1169  used, used_name = used # we re-set used to re-use the code below
1170  if not used_name:
1171  used_name = self._instanceName(used)
1172  # Check is 'used' is a string or not
1173  if type(used) is str:
1174  used_class = confDbGetConfigurable(used)
1175  else:
1176  used_class = used
1177  # Instantiate the configurable that we are going to use
1178  try:
1179  inst = used_class(name = used_name, _enabled = False)
1180  except AttributeError:
1181  # This cover the case where the used configurable is not a
1182  # ConfigurableUser instance, i.e. id doesn't have the attribute
1183  # '_enabled'.
1184  inst = used_class(name = used_name)
1185  self.__addActiveUseOf(inst)
1186  for queried in self.__queried_configurables__:
1187  try:
1188  if type(queried) is str:
1189  queried = confDbGetConfigurable(queried)
1190  inst = queried(_enabled = False)
1191  except AttributeError:
1192  inst = queried()
1193  self.__addPassiveUseOf(inst)
1194  def __addActiveUseOf(self, other):
1195  """
1196  Declare that we are going to modify the Configurable 'other' in our
1197  __apply_configuration__.
1198  """
1199  self.__used_instances__.append(other)
1200  if hasattr(other, "__users__"): # allow usage of plain Configurables
1201  other.__users__.append(self)
1202  def __addPassiveUseOf(self, other):
1203  """
1204  Declare that we are going to retrieve property values from the
1205  ConfigurableUser 'other' in our __apply_configuration__.
1206  """
1207  if not isinstance(other, ConfigurableUser):
1208  raise Error("'%s': Cannot make passive use of '%s', it is not a ConfigurableUser" % (self.name(), other.name()))
1209  other.__addActiveUseOf(self)
1210  def getGaudiType( self ):
1211  return 'User'
1212  def getDlls( self ):
1213  return None
1214  def getHandle( self ):
1215  return None
1216 
1217  def __detach_used__(self):
1218  """
1219  Remove this ConfigurableUser instance from the users list of the used
1220  instances.
1221  """
1222  for used in self.__used_instances__:
1223  if hasattr(used, "__users__"): # allow usage of plain Configurables
1224  used.__users__.remove(self)
1225 
1226  def propagateProperty(self, name, others = None, force = True):
1227  """
1228  Propagate the property 'name' (if set) to other configurables (if possible).
1229  'others' can be:
1230  None:
1231  propagate to all the entries in __used_configurables__
1232  a configurable instance:
1233  propagate only to it
1234  list of configurable instances:
1235  propagate to all of them.
1236 
1237 
1238  The logic is:
1239  - if the local property is set, the other property will be overwritten
1240  - local property not set and other set => keep other
1241  - local property not set and other not set => overwrite the default for
1242  ConfigurableUser instances and set the property for Configurables
1243  """
1244  # transform 'others' to a list of configurable instances
1245  if others is None:
1246  others = self.__used_instances__
1247  elif type(others) not in [ list, tuple ] :
1248  others = [ others ]
1249  # these can be computed before the loop
1250  local_is_set = self.isPropertySet(name)
1251  value = self.getProp(name)
1252  # loop over the others that do have 'name' in their slots
1253  for other in [ o for o in others if name in o.__slots__ ]:
1254  # If self property is set, use it
1255  if local_is_set:
1256  if other.isPropertySet(name):
1257  log.warning("Property '%(prop)s' is set in both '%(self)s' and '%(other)s', using '%(self)s.%(prop)s'"%
1258  { "self": self.name(),
1259  "other": other.name(),
1260  "prop": name } )
1261  other.setProp(name, value)
1262  # If not, and other property also not set, propagate the default
1263  elif not other.isPropertySet(name):
1264  if isinstance(other,ConfigurableUser):
1265  otherType = type(other._properties[name].getDefault())
1266  other._properties[name].setDefault(value)
1267  if otherType in [list, dict]:
1268  # Special case for list and dictionaries:
1269  # also set the property to the same value of the default (copy)
1270  other.setProp(name, otherType(value))
1271  else:
1272  other.setProp(name, value)
1273  # If not set and other set, do nothing
1274 
1275  def propagateProperties(self, names = None, others = None, force = True):
1276  """
1277  Call propagateProperty for each property listed in 'names'.
1278  If 'names' is None, all the properties are propagated.
1279  """
1280  if names is None:
1281  # use all the non-private slots
1282  names = [ p for p in self.__slots__ if not p.startswith("_") ]
1283  for n in names:
1284  self.propagateProperty(n, others, force)
1285 
1287  """
1288  Function to be overridden to convert the high level configuration into a
1289  low level one.
1290  The default implementation calls applyConf, which is the method defined
1291  in some ConfigurableUser implementations.
1292  """
1293  return self.applyConf()
1294 
1295  def applyConf( self ):
1296  """
1297  Function to be overridden to convert the high level configuration into a
1298  low level one.
1299  """
1300  pass
1301 
1302  def _instanceName(self, cls):
1303  """
1304  Function used to define the name of the private instance of a given class
1305  name.
1306  This method is used when the __used_configurables_property__ declares the
1307  need of a private used configurable without specifying the name.
1308  """
1309  if type(cls) is str:
1310  clName = cls
1311  else:
1312  clName = cls.__name__
1313  return "%s_%s" % (self.name(), clName)
1314 
1315  def getUsedInstance(self, name):
1316  """
1317  Return the used instance with a given name.
1318  """
1319  for i in self.__used_instances__:
1320  if i.name() == name:
1321  if hasattr(i, "_enabled"):
1322  # ensure that the instances retrieved through the method are
1323  # enabled
1324  i._enabled = True
1325  return i
1326  raise KeyError(name)
1327 
1328  def isApplicable(self):
1329  '''
1330  Return True is the instance can be "applied".
1331  '''
1332  return (not self.__users__) and (not self._applied)
1333 
1334 
1335 # list of callables to be called after all the __apply_configuration__ are called.
1336 postConfigActions = []
1338  """
1339  Add a new callable ('function') to the list of post-configuration actions.
1340  If the callable is already in the list, it is moved to the end of the list.
1341  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1342  """
1343  try:
1344  postConfigActions.remove(function)
1345  except:
1346  pass
1347  postConfigActions.append(function)
1349  """
1350  Remove a callable from the list of post-config actions.
1351  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1352  """
1353  postConfigActions.remove(function)
1354 
1355 _appliedConfigurableUsers_ = False
1357  """
1358  Call the apply method of all the ConfigurableUser instances respecting the
1359  dependencies. First the C.U.s that are not used by anybody, then the used
1360  ones, when they are not used anymore.
1361  """
1362  # Avoid double calls
1363  global _appliedConfigurableUsers_, postConfigActions
1364  if _appliedConfigurableUsers_:
1365  return
1366  _appliedConfigurableUsers_ = True
1367 
1368  def applicableConfUsers():
1369  '''
1370  Generator returning all the configurables that can be applied in the
1371  order in which they can be applied.
1372  '''
1373  # This is tricky...
1374  # We keep on looking for the first configurable that can be applied.
1375  # When we cannot find any, 'next()' raises a StopIteration that is
1376  # propagated outside of the infinite loop and the function, then handled
1377  # correctly from outside (it is the exception that is raised when you
1378  # exit from a generator).
1379  # Checking every time the full list is inefficient, but it is the
1380  # easiest way to fix bug #103803.
1381  # <https://savannah.cern.ch/bugs/?103803>
1382  while True:
1383  yield (c for c in Configurable.allConfigurables.values()
1384  if c.isApplicable()).next()
1385 
1386  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1387  for c in applicableConfUsers():
1388  if c._enabled:
1389  log.info("applying configuration of %s", c.name())
1390  if debugApplyOrder:
1391  sys.stderr.write('applying %r' % c)
1392  c.__apply_configuration__()
1393  log.info(c)
1394  else:
1395  log.info("skipping configuration of %s", c.name())
1396  c._applied = True # flag the instance as already applied
1397  if hasattr(c, "__detach_used__"):
1398  # tells the used configurables that they are not needed anymore
1399  c.__detach_used__()
1400 
1401  # check for dependency loops
1402  leftConfUsers = [c for c in Configurable.allConfigurables.values()
1403  if hasattr(c, '__apply_configuration__') and
1404  c._enabled and not c._applied]
1405  # if an enabled configurable has not been applied, there must be a dependency loop
1406  if leftConfUsers:
1407  raise Error("Detected loop in the ConfigurableUser"
1408  " dependencies: %r" % [ c.name()
1409  for c in leftConfUsers ])
1410  # ensure that all the Handles have been triggered
1411  known = set()
1412  unknown = set(Configurable.allConfigurables)
1413  while unknown:
1414  for k in unknown:
1415  if not known: # do not print during the first iteration
1416  log.debug('new configurable created automatically: %s', k)
1417  # this trigger the instantiation from handles
1418  Configurable.allConfigurables[k].properties()
1419  known.add(k)
1420  unknown -= known
1421  # Call post-config actions
1422  for action in postConfigActions:
1423  action()
1424 
1426  """
1427  Obsolete (buggy) implementation of applyConfigurableUsers(), kept to provide
1428  backward compatibility for configurations that where relying (implicitly) on
1429  bug #103803, or on a specific (non guaranteed) order of execution.
1430 
1431  @see applyConfigurableUsers()
1432  """
1433  # Avoid double calls
1434  global _appliedConfigurableUsers_, postConfigActions
1435  if _appliedConfigurableUsers_:
1436  return
1437  _appliedConfigurableUsers_ = True
1438 
1439  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1440  confUsers = [ c
1441  for c in Configurable.allConfigurables.values()
1442  if hasattr(c,"__apply_configuration__") ]
1443  applied = True # needed to detect dependency loops
1444  while applied and confUsers:
1445  newConfUsers = [] # list of conf users that cannot be applied yet
1446  applied = False
1447  for c in confUsers:
1448  if hasattr(c,"__users__") and c.__users__:
1449  newConfUsers.append(c) # cannot use this one yet
1450  else: # it does not have users or the list is empty
1451  applied = True
1452  # the ConfigurableUser is enabled if it doesn't have an _enabled
1453  # property or its value is True
1454  enabled = (not hasattr(c, "_enabled")) or c._enabled
1455  if enabled:
1456  log.info("applying configuration of %s", c.name())
1457  if debugApplyOrder:
1458  sys.stderr.write('applying %r' % c)
1459  c.__apply_configuration__()
1460  log.info(c)
1461  else:
1462  log.info("skipping configuration of %s", c.name())
1463  if hasattr(c, "__detach_used__"):
1464  # tells the used configurables that they are not needed anymore
1465  c.__detach_used__()
1466  confUsers = newConfUsers # list of C.U.s still to go
1467  if confUsers:
1468  # this means that some C.U.s could not be applied because of a dependency loop
1469  raise Error("Detected loop in the ConfigurableUser "
1470  " dependencies: %r" % [ c.name()
1471  for c in confUsers ])
1472  # ensure that all the Handles have been triggered
1473  known = set()
1474  unknown = set(Configurable.allConfigurables)
1475  while unknown:
1476  for k in unknown:
1477  if not known: # do not print during the first iteration
1478  log.debug('new configurable created automatically: %s', k)
1479  # this trigger the instantiation from handles
1480  Configurable.allConfigurables[k].properties()
1481  known.add(k)
1482  unknown -= known
1483  # Call post-config actions
1484  for action in postConfigActions:
1485  action()
1486 
1488  """
1489  Function to select all and only the configurables that have to be used in
1490  GaudiPython.AppMgr constructor.
1491  This is needed because in Athena the implementation have to be different (the
1492  configuration is used in a different moment).
1493  """
1494  return [ k
1495  for k, v in Configurable.allConfigurables.items()
1496  if v.getGaudiType() != "User" ] # Exclude ConfigurableUser instances
1497 
1498 def purge():
1499  """
1500  Clean up all configurations and configurables.
1501  """
1502  for c in Configurable.allConfigurables.values():
1503  c.__class__.configurables.clear()
1504  Configurable.allConfigurables.clear()
1505  # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
1506  # migrated to the correct class when this is known.
1507  ConfigurableGeneric.configurables.clear()
1508  from ProcessJobOptions import _included_files
1509  import os.path, sys
1510  for file in _included_files:
1511  dirname, basname = os.path.split(file)
1512  basname, ext = os.path.splitext(basname)
1513  if basname in sys.modules:
1514  del sys.modules[basname]
1515  _included_files.clear()
string action
Definition: merge_files.py:103
list __queried_configurables__
list of ConfigurableUser classes this one is going to query in the apply_configuration method ...
def _printHeader(indentStr, title)
def __init__(self, name=Configurable.DefaultName, _enabled=True, kwargs)
def _printFooter(indentStr, title)
def clone(self, name=None, kwargs)
list __used_configurables__
list of ConfigurableUser classes this one is going to modify in the apply_configuration method...
def appendPostConfigAction(function)
def copyChildAndSetParent(self, cfg, parent)
def isApplicable(self)
if isinstance(v,Configurable) and not v.isPublic(): rep += v.__str__( indent + 1 ) + os...
this metaclass installs PropertyProxy descriptors for Gaudi properties
iAlgorithm
The basic module.
Definition: GaudiAlgs.py:54
def removePostConfigAction(function)
classes for generic Gaudi component ===========
def __setattr__(self, name, value)
string type
Definition: gaudirun.py:151