| # Gaudi Framework, version v25r0 |

Home |  | Generated: Mon Feb 17 2014

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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
102 __metaclass__ = ConfigurableMeta.ConfigurableMeta
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
602 def getDefaultProperties( cls ):
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
931 class ConfigurableGeneric( Configurable ):
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
990 class ConfigurableService( Configurable ):
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
1014 class ConfigurableAlgTool( Configurable ):
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...)
1096 class ConfigurableAuditor( Configurable ):
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
1120 class ConfigurableUser( Configurable ):
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 #
1151 self.__used_instances__ = []
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
1275 def __apply_configuration__(self):
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 = []
1326 def appendPostConfigAction(function):
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)
1337 def removePostConfigAction(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
1345 def applyConfigurableUsers():
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
1414 def applyConfigurableUsers_old():
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
1476 def getNeededConfigurables():
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()