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