Gaudi Framework, version v25r0

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

Generated at Mon Feb 17 2014 14:37:44 for Gaudi Framework, version v25r0 by Doxygen version 1.8.2 written by Dimitri van Heesch, © 1997-2004