ATLAS Offline Software
Loading...
Searching...
No Matches
Configurable.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3# File: AthenaCommon/python/Configurable.py
4# Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
5# Author: Martin Woudstra (Martin.Woudstra@cern.ch)
6
7import copy, os, weakref
8from AthenaCommon import ConfigurableMeta
9
10# Note: load iProperty etc. from GaudiPython only as-needed
11import GaudiKernel.GaudiHandles as GaudiHandles
12import GaudiKernel.DataHandle as DataHandle
13from collections.abc import MutableSequence
14
15
16__version__ = '3.2.0'
17__author__ = 'Wim Lavrijsen (WLavrijsen@lbl.gov)'
18
19__all__ = [ 'Configurable',
20 'ConfigurableAlgorithm', 'ConfigurablePyAlgorithm',
21 'ConfigurableAlgTool',
22 'ConfigurableAuditor',
23 'ConfigurableService',
24 'ConfigurableUser'
25 ]
26
27
28
29from AthenaCommon.Logging import logging
30log = logging.getLogger( 'Configurable' )
31
32
33# Context Manager to set Configurable behavior
35 def __init__(self, target_state=True):
36 self._target_state = target_state
37 self._previous_state = None
38 def __enter__(self):
39 self._previous_state = Configurable._useGlobalInstances
40 # CA does not use global instances:
41 Configurable._useGlobalInstances = not self._target_state
42 def __exit__(self, exception_type, exception_value, traceback):
43 Configurable._useGlobalInstances = self._previous_state
44
45
46
48 """Base class for Gaudi components that implement the IProperty interface.
49 Provides most of the boilerplate code, but the actual useful classes
50 are its derived ConfigurableAlgorithm, ConfigurableService, and
51 ConfigurableAlgTool."""
52
53 # for detecting the default name
55 pass
56
57 # for collecting default values
59 pass
60
61 propertyNoValue = '<no value>'
62 indentUnit = '| '
63 printHeaderWidth=100
64 printHeaderPre=5
65
66 _fInSetDefaults = 0x01 # currently setting default values
67 _fIsLocked = 0x02 # track configurable to be locking
68 _fIsPrinting = 0x04 # inside __str__ to avoid setting of private AlgTools
69 _fInitOk = 0x08 # used to enforce base class init
70 _fSetupOk = 0x10 # for debugging purposes (temporary (?))
71
72 __slots__ = (
73 '__weakref__', # required for dealing with weak references
74 '__children', # controlled components, e.g. private AlgTools
75 '_name', # the (unqualified) component name
76 '_flags', # see list of flags above
77 )
78
79 allConfigurables = weakref.WeakValueDictionary() # just names would do,
80 # but currently refs to the actual configurables
81 # are used for debugging purposes
82
83 _printOnce = 0
84
85 # The default is the pre-ComponentAccumulator behavior, i.e. use
86 # a global namespace for configurable instances:
87 _useGlobalInstances = True
88
89 def __new__ ( cls, *args, **kwargs ):
90 """To Gaudi, any object with the same type/name is the same object. Hence,
91 this is mimicked in the configuration: instantiating a new Configurable
92 of a type with the same name will return the same instance."""
93
94 # try to get the name of the Configurable (having a name is compulsory)
95 if 'name' in kwargs:
96 # simple keyword (by far the easiest)
97 name = kwargs[ 'name' ]
98 elif 'name' in cls.__init__.__code__.co_varnames:
99 # either positional in args, or default
100 index = list(cls.__init__.__code__.co_varnames).index( 'name' )
101 try:
102 # var names index is offset by one as __init__ is to be called with self
103 name = args[ index - 1 ]
104 except IndexError:
105 # retrieve default value, then
106 name = cls.__init__.__defaults__[ index - (len(args)+1) ]
107 else:
108 # positional index is assumed (will work most of the time)
109 try:
110 name = args[1] # '0' is for self
111 except (IndexError,TypeError):
112 raise TypeError( 'no "name" argument while instantiating "%s"' % cls.__name__ )
113
114 if name == Configurable.DefaultName:
115 # select either conventional name, or the type of the class
116 if hasattr( cls, 'DefaultedName' ):
117 name = cls.DefaultedName
118 else:
119 name = cls.getType()
120 elif not name or type(name) is not str:
121 # unnamed, highly specialized user code, etc. ... unacceptable
122 raise TypeError( 'could not retrieve name from %s.__init__ arguments' % cls.__name__ )
123
124 # close backdoor access to otherwise private subalgs/tools
125 if 0 <= name.find( '.' ):
126 raise NameError( '"%s": Gaudi name indexing with "." to private configurables not '
127 'allowed, as it leads to uncheckable backdoors' % name )
128
129 if 0 <= name.find( '/' ):
130 raise NameError( '"%s": type separator "/" no allowed in component name, '
131 'typename is derived from configurable instead' % name )
132
133 if cls._useGlobalInstances:
134 # ordinary recycle case
135 try:
136 conf = cls.configurables[ name ]
137
138 # initialize additional properties, if any
139 for n,v in kwargs.items():
140 try:
141 setattr( conf, n, v )
142 except AttributeError as originalAttributeError:
143
144 # now for a completely different can of worms ...
145 acceptableKeyWord = False
146
147 # little helper
148 def flat( classtree ):
149 if isinstance(classtree, (list, tuple)):
150 return [ j for i in classtree for j in flat( i ) ]
151 else:
152 return [ classtree ]
153
154 # the following attempts to figure out if the missing attribute
155 # is in fact an acceptable formal function argument to __init__,
156 import inspect
157 allclasses = flat( inspect.getclasstree( [ conf.__class__ ] ) )
158 for confklass in allclasses:
159 try:
160 # the following may result in the same init tried several
161 # times, but we shouldn't be in this loop too often anyway
162 if n in confklass.__init__.__code__.co_varnames:
163 acceptableKeyWord = True
164 break
165 except AttributeError:
166 pass
167
168 # raising here will break the usage of keywords in __init__, but
169 # there don't appear to be such cases in ATLAS yet ...
170 if not acceptableKeyWord:
171 raise originalAttributeError
172 return conf
173 except KeyError:
174 pass
175 else:
176 #Run 3 style config
177 #Uncomment this line to verify that RecExCommon doesn't use that
178 #print "Run 3 style config"
179 pass #end if not new configuration approach
180 # still here: create a new instance ...
181 conf = object.__new__( cls )
182
183 # ... python convention says to silently return, if __new__ fails ...
184 if conf is None:
185 return
186
187 # ... initialize it
188 cls.__init__( conf, *args, **kwargs )
189
190 # update normal, per-class cache
191 if cls._useGlobalInstances:
192 cls.configurables[ name ] = conf
193
194 # update generics super-cache
195 if name in cls.allConfigurables and conf.getType() != cls.allConfigurables[ name ].getType():
196 raise TypeError( 'attempt to redefine type of "%s" (was: %s, new: %s)' %
197 (name,cls.allConfigurables[ name ].getType(), conf.getType()) )
198 cls.allConfigurables[ name ] = conf
199
200 return conf
201
202 # constructor
203 def __init__( self, name = DefaultName ):
204 # check class readiness, all required overloads should be there now
205 klass = self.__class__
206
207 # this is an abstract class
208 if klass == Configurable:
209 raise TypeError( "%s is an ABC and can not be instantiated" % str(Configurable))
210
211 # for using this Configurable as a (Gaudi) sequence
212 self.__children = []
213
214 # know who we are
215 if name == Configurable.DefaultName:
216 if hasattr( self.__class__, 'DefaultedName' ):
217 self._name = self.__class__.DefaultedName
218 else:
219 self._name = self.getType()
220 else:
221 self._name = name
222
223 self._flags = 0
224
225 # set to True when collecting defaults, False otherwise
226 self._flags &= ~self._fInSetDefaults
227
228 # set to True when locked, False otherwise
229 self._flags &= ~self._fIsLocked
230
231 # set to True when inside __str__ (volatile - not stored on file)
232 self._flags &= ~self._fIsPrinting
233
234 # for later, in case __init__ itself is overridden
235 self._flags |= self._fInitOk
236
237 # for debugging purposes (temporary (?))
238 self._flags &= ~self._fSetupOk
239
240 # pickle support
241 def __getstate__( self ):
242 d = {}
243 for name, proxy in self._properties.items():
244 try:
245 d[ name ] = proxy.__get__( self )
246 except AttributeError:
247 pass
248
249 d[ '_Configurable__children' ] = self.__children
250 d[ '_name' ] = self._name
251 if hasattr(self, '_jobOptName'):
252 d[ '_jobOptName' ] = self.getJobOptName()
253 d[ '_fIsLocked' ] = self.isLocked()
254
255 return d
256
257 def __setstate__( self, dct ):
258 # flags are set to neutral, not from pickle except for lockedness
259 self._flags = 0
260 self._flags |= self._fInitOk
261 self._flags &= ~self._fInSetDefaults
262 if '_fIsLocked' in dct:
263 if dct[ '_fIsLocked' ]:
264 self._flags &= self._fIsLocked
265 else:
266 self._flags &= ~self._fIsLocked
267 del dct['_fIsLocked']
268 self._flags &= ~self._fIsPrinting
269 for (n, v) in dct.items():
270 setattr( self, n, v )
271
272 return
273
274 def __getnewargs__( self ):
275 return (self._name,)
276
277 # to allow a few basic sanity checks, as well as nice syntax
278 def __len__( self ):
279 return len( self.__children )
280
281 def __iter__( self ):
282 return iter( self.__children )
283
284 # ownership rules of self through copying
285 def __deepcopy__( self, memo ):
286 newconf = object.__new__( self.__class__ )
287 self.__class__.__init__( newconf, self.getName() )
288
289 for proxy in self._properties.values():
290 try:
291 proxy.__set__( newconf, proxy.__get__( self ) )
292 except AttributeError:
293 pass # means property was not set for self
294
295 for c in self.__children:
296 newconf += c # processes proper copy semantics
297
298 return newconf
299
300 # hierarchy building, and ownership rules of children
301 def __iadd__( self, configs, descr = None, index = None ):
302 if self.isLocked():
303 raise RuntimeError( f"cannot add children to locked configurable {self.getJobOptName()}" )
304
305 if not type(configs) in (list,tuple):
306 configs = ( configs, )
307
308 joname = self.getJobOptName()
309
310 for cfg in configs:
311 # prevent type mismatches
312 if not isinstance( cfg, Configurable ):
313 raise TypeError( "'%s' is not a Configurable" % str(cfg) )
314
315 cc = self.copyChildAndSetParent( cfg, joname )
316
317 # filters dupes; usually "ok" (backdoor should catch them)
318 ccjo = cc.getJobOptName()
319 for c in self.__children:
320 if c.getJobOptName() == ccjo:
321 log.debug( 'attempt to add a duplicate (%s.%s) ... dupe ignored', joname or self.name(), ccjo )
322 break
323 else:
324 if index is None:
325 self.__children.append( cc )
326 else:
327 self.__children.insert( index, cc )
328
329 try:
330 if descr: # support for tool properties
331 descr.__set__( self, cc )
332 else:
333 setattr( self, cc.getName(), cc )
334 except AttributeError:
335 pass # to allow free addition of tools/subalgorithms
336
337 return self
338
339 def __getattr__( self, attr ): # until ToolProperties exist ...
340 if attr[0] == "_":
341 return super( Configurable, self ).__getattribute__(attr)
342 for c in self.__children:
343 if c.getName() == attr:
344 return c
345
346 raise AttributeError( "'%s' object has no attribute '%s'" % (self.__class__,attr) )
347
348 def __delattr__( self, attr ):
349 # remove as property, otherwise try as child
350 try:
351 # remove history etc., then reset to default (in case set before)
352 prop = self._properties[ attr ]
353 prop.__delete__( self )
354 prop.__set__( self, prop.default )
355 return # reaches here? was property: done now
356 except KeyError:
357 pass
358
359 # otherwise, remove child, if one is so named
360 for c in self.__children:
361 if c.getName() == attr:
362 self.__children.remove( c )
363
364 # potentially, there are left over caches (certain user derived classes)
365 try:
366 del self.__dict__[ attr ]
367 except (AttributeError,KeyError):
368 pass
369
370 def remove( self, items ):
371 if not isinstance(items, (list, tuple)):
372 items = [ items ]
373
374 self.__children = [ e for e in self.__children if e not in items ]
375
376 def removeAll( self ):
377 self.remove( self.__children )
378
379 # to copy a full configuration
380 def clone( self, newname ):
381 if newname == self.getName():
382 log.error( 'not cloning %s, because new name given is same as old name', self.getName() )
383 return None
384
385 return self.__class__( newname, **self.getValuedProperties() )
386
387 # called by __iadd__; determines child copy semantics
388 def copyChild( self, child ):
389 return copy.deepcopy( child )
390
391 def setParent( self, parentName ):
392 pass
393
394 def getParent( self ):
395 return ""
396
397 def hasParent( self, parent ):
398 return False
399
400 def copyChildAndSetParent(self,cfg,parent):
401 cc = self.copyChild( cfg )
402
403 if hasattr( cc, 'setParent' ) and parent:
404 cc.setParent( parent )
405
406 return cc
407
408 def getChildren( self ):
409 return self.__children[:] # read only
410
411 def overwriteChild( self, idx, newChild ):
412 self.__children[idx] = newChild
413
414 def getAllChildren( self ):
415 """Get all (private) configurable children, both explicit ones (added with +=)
416 and the ones in the private GaudiHandle properties"""
417 childs = []
418 # add private configurable properties (also inside handles)
419 for proxy in self._properties.values():
420 try:
421 c = proxy.__get__( self )
422 except AttributeError:
423 pass
424 else:
425 if isinstance(c,Configurable) and not c.isPublic():
426 childs.append(c)
427 elif isinstance(c,GaudiHandles.GaudiHandle):
428 try:
429 conf = c.configurable
430 except AttributeError:
431 pass
432 else:
433 if not conf.isPublic():
434 childs.append(conf)
435 elif isinstance(c,GaudiHandles.GaudiHandleArray):
436 # only setup private arrays
437 if not c.isPublic():
438 for ci in c:
439 if isinstance(ci,Configurable):
440 childs.append(ci)
441 else:
442 try:
443 conf = ci.configurable
444 except AttributeError:
445 pass
446 else:
447 childs.append(conf)
448
449 # add explicit children
450 childs += self.__children
451 return childs
452
453 def getSequence( self ):
454 elems = []
455 for c in self.__children:
456 elems.append( c.getFullName() )
457 return elems
458
459 def setup( self ):
460 # make sure base class init has been called
461 if not hasattr(self,'_fInitOk') or not self._fInitOk:
462 # could check more, but this is the only explanation
463 raise TypeError("Configurable.__init__ not called in %s override" % self.__class__.__name__)
464
465 # setup self: this collects all values on the python side
466 self.__setupDlls()
467 self.__setupDefaults()
468
469 # setup children
470 for c in self.getAllChildren():
471 c.setup()
472
473 # now get handle to work with for moving properties into the catalogue
474 handle = self.getHandle()
475 if not handle:
476 log.debug( 'no handle for %s: not transporting properties', self._name )
477 return # allowed, done early
478
479 # pass final set of properties on to handle on the C++ side or JobOptSvc
480 for name in self._properties.keys():
481 if hasattr( self, name ): # means property has python-side value/default
482 setattr( handle, name, getattr(self,name) )
483
484 # for debugging purposes
485 self._flags |= self._fSetupOk
486
487 def lock( self ):
488 # prevent any further changes, unless unlocked
489 self._flags |= self._fIsLocked
490
491 # same for any of its private ToolHandle properties
492 for k, v in self.getProperties().items():
493 if isinstance( v, Configurable ):
494 v.lock()
495
496 # leave children alone ... most likely are public (and can't check otherwise)
497
498 def unlock( self ):
499 # allow again changes to be made
500 import sys, traceback
501 stack = traceback.extract_stack( sys._getframe(1), 1 )
502 log.warning( 'unlock() called on configurable "%s" in %s', self.getJobOptName(), stack[0][0] )
503 self._flags &= ~self._fIsLocked
504
505 # note that unlock() does not unlock the children; do that individually
506
507 def isLocked( self ):
508 # check whether this configurable is currently locked
509 return self._flags & self._fIsLocked
510
511 def isPrinting( self ):
512 # check whether this configurable is currently being printed (see PropertyProxy.py)
513 return self._flags & self._fIsPrinting
514
515 def getProperties( self ):
516 props = {}
517 for name, proxy in self._properties.items():
518 try:
519 props[ name ] = proxy.__get__( self )
520 except AttributeError:
521 props[ name ] = Configurable.propertyNoValue
522
523 return props
524
526 props = {}
527 for name, proxy in self._properties.items():
528 try:
529 value = proxy.__get__( self )
530 if hasattr(value, 'getFullName') :
531 value = value.getFullName()
532 elif type(value) is list and len(value) > 0 and hasattr(value[0], 'getFullName'):
533 value = [ i.getFullName() for i in value ]
534 props[ name ] = value
535 except AttributeError:
536 pass
537
538 return props
539
540 def properties( self ):
541 return self.getProperties() # compatibility
542
543 @classmethod
545
546 # user provided defaults
548 cls.setDefaults( c )
549
550 # defaults from C++
551 for k,v in cls._properties.items():
552 if k not in c.__dict__ and hasattr( v, 'default' ):
553 c.__dict__[ k ] = v.default
554
555 return c.__dict__
556
557 @classmethod
558 def getDefaultProperty( cls, name ):
559
560 # user provided defaults
562 cls.setDefaults( c )
563
564 if name in c.__dict__:
565 return c.__dict__[ name ]
566
567 # defaults from C++
568 try:
569 v = cls._properties[name]
570 if hasattr( v, 'default' ):
571 return v.default
572 except KeyError:
573 pass
574
575 return None
576
577 def getType( cls ):
578 return cls.__name__
579
580 def getName( self ):
581 return self._name
582
583 def name( self ):
584 return self.getName()
585
586 def getJobOptName( self ): # full hierachical name
587 return self.getName()
588
589 def isPublic( self ):
590 return True
591
592 # for a couple of existing uses out there
593 def getFullName( self ) :
594 return str( self.getType() + '/' + self.getName() )
595
596 def getFullJobOptName( self ):
597 return "%s/%s" % (self.getType(),self.getJobOptName() or self.getName())
598
599 def getPrintTitle(self):
600 return self.getGaudiType() + ' ' + self.getTitleName()
601
602 def getTitleName( self ):
603 if log.isEnabledFor( logging.DEBUG ):
604 return self.getFullJobOptName()
605 else:
606 return self.getFullName()
607
608 def setDefaults( cls, handle ):
609 pass
610
611 def _isInSetDefaults( self ):
612 return self._flags & self._fInSetDefaults
613
614 def __setupDlls( self ):
615 dlls = self.getDlls()
616 if not dlls:
617 dlls = []
618 elif isinstance(dlls, str):
619 dlls = [ dlls ]
620
621 from AthenaCommon.AppMgr import theApp
622 dlls = filter( lambda d: d not in theApp.Dlls, dlls )
623 if dlls: theApp.Dlls += dlls
624
625 def __setupDefaults( self ):
626 # set handle defaults flags to inform __setattr__ that it is being
627 # called during setDefaults of the concrete Configurable
628 self._flags |= self._fInSetDefaults
629 self.setDefaults( self )
630 self._flags &= ~self._fInSetDefaults
631
632 @staticmethod
633 def _printHeader( indentStr, title ):
634 preLen = Configurable.printHeaderPre
635 postLen = Configurable.printHeaderWidth - preLen - 3 - len(title)# - len(indentStr)
636 postLen = max(preLen,postLen)
637 return indentStr + '/%s %s %s' % (preLen*'*',title,postLen*'*')
638
639 @staticmethod
640 def _printFooter( indentStr, title ):
641 preLen = Configurable.printHeaderPre
642 postLen = Configurable.printHeaderWidth - preLen - 12 - len(title)# - len(indentStr)
643 postLen = max(preLen,postLen)
644 return indentStr + '\\%s (End of %s) %s' % (preLen*'-',title,postLen*'-')
645
646 def __repr__( self ):
647 return '<%s at %s>' % (self.getFullJobOptName(),hex(id(self)))
648
649 def __str__( self, indent = 0, headerLastIndentUnit=indentUnit ):
650 def _sorted_repr_set(value):
651 """Helper to print sorted set representation"""
652 return "{" + repr(sorted(value))[1:-1] + "}" if value else "set()"
653
654 indentStr = indent*Configurable.indentUnit
655 # print header
656 title = self.getPrintTitle()
657
658 # if run under WARNING and hight only, stick with title
659 if logging.WARNING <= log.level:
660 if not Configurable._printOnce:
661 Configurable._printOnce = 1
662 return "Reduced Configurable printout; change log level for more details, e.g.:\n import AthenaCommon.Configurable as Configurable\n Configurable.log.setLevel( INFO )\n" + title
663 else:
664 return title
665
666 # avoid auto-setting private AlgTools while doing printout
667 self._flags |= self._fIsPrinting
668
669 # print line to easily see start-of-configurable
670 if indent > 0:
671 headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
672 else:
673 headerIndent = ''
674 rep = Configurable._printHeader( headerIndent, title )
675 rep += os.linesep
676 # print own properties
677 props = self.getProperties()
678 defs = self.getDefaultProperties()
679 if not props:
680 rep += indentStr + '|-<no properties>' + os.linesep
681 else:
682 # get property name with
683 nameWidth = 0
684 for p in props.keys():
685 nameWidth=max(nameWidth,len(p))
686 propNames = sorted(props.keys())
687 for p in propNames:
688 v = props[p]
689 # start with indent and property name
690 prefix = indentStr + '|-%-*s' % (nameWidth,p)
691 # add memory address for debugging (not for defaults)
692 if log.isEnabledFor( logging.DEBUG ):
693 if v != Configurable.propertyNoValue:
694 address = ' @%11s' % hex(id(v))
695 else:
696 address = 13*' '
697 prefix += address
698 # add value and default
699 default = defs.get(p)
700
701 if not isinstance(v,MutableSequence) and v == Configurable.propertyNoValue:
702 # show default value as value, and no extra 'default'
703 strVal = repr(default)
704 strDef = None
705 else:
706 # convert configurable to handle
707 if isinstance(v,Configurable):
708 vv = v.getGaudiHandle()
709 else:
710 vv = v
711 if isinstance(vv,(GaudiHandles.GaudiHandle,
712 GaudiHandles.GaudiHandleArray,
713 DataHandle.DataHandle)):
714 strVal = repr(vv)
715 strDef = repr(default.toStringProperty())
716 if strDef == repr(vv.toStringProperty()):
717 strDef = None
718 elif isinstance(vv, set):
719 strVal = _sorted_repr_set(vv)
720 strDef = _sorted_repr_set(default)
721 else:
722 strVal = repr(vv)
723 strDef = repr(default)
724 # add the value
725 line = prefix + ' = ' + strVal
726 # add default if present
727 if strDef is not None:
728 # put default on new line if too big
729 if len(line) + len(strDef) > Configurable.printHeaderWidth:
730 line += os.linesep + indentStr + '| ' + (len(prefix)-len(indentStr)-3)*' '
731 line += ' (default: %s)' % (strDef,)
732 # add the line to the total string
733 rep += line + os.linesep
734
735 # print configurables + their properties, or loop over sequence
736 childNotToSort = []
737 childToSort = []
738 for c in self.getAllChildren():
739 if isinstance(c, ConfigurableAlgorithm): childNotToSort.append(c)
740 else: childToSort.append(c)
741 # sort the non-algs
742 childToSort.sort(key=lambda x : x.getName())
743 for cfg in childToSort + childNotToSort:
744 rep += cfg.__str__( indent + 1, '|=' ) + os.linesep
745
746 # print line to easily see end-of-configurable. Note: No linesep!
747 rep += Configurable._printFooter( indentStr, title )
748
749 self._flags &= ~self._fIsPrinting
750 return rep
751
752 # hash method for set/dict operations
753 # first attempt, assuming need to recurse into child properties
754 # if too much overhead, could attempt to cache with python
755 # properties, but hard to propagate changes upwards to parents
756
758 self._flags |= self._fIsPrinting
759 properties = self.getValuedProperties()
760 propstr = ""
761 for key,val in sorted(properties.items()):
762 if isinstance(val, (GaudiHandles.PublicToolHandle, GaudiHandles.PrivateToolHandle)):
763 propstr += val.getFullName()
764 elif isinstance(val,Configurable):
765 propstr += "({0}:{1})".format(key,val.getFlattenedProperties())
766 elif isinstance(val, (GaudiHandles.PublicToolHandleArray, GaudiHandles.PrivateToolHandleArray)):
767 for th in val:
768 # Handle ToolHandles that have just been set as strings(?)
769 if isinstance(th,Configurable):
770 propstr += "({0}:{1}".format(th.getFullName(), th.getFlattenedProperties())
771 else:
772 propstr += th.getFullName()
773 else:
774 propstr += "({0}:{1})".format(key,str(val))
775 self._flags &= ~self._fIsPrinting
776 return propstr
777
779
780 descr = ""
781 if hasattr( self,"_name" ):
782 propstr = self.getFlattenedProperties()
783 descr = (self.getFullName(), propstr)
784 else: # Not yet initialised?
785 descr = self.getType()
786
787 return descr
788
789 # # (in)equality operators, based on hash
790 def __eq__(self,rhs):
791 # Check identity first
792 if self is rhs: return True
793 # Avoid comparing against None...
794 if not rhs: return False
795 # Class check
796 if not isinstance(rhs,Configurable): return False
797 # Type/Name check
798 if self.getFullName() != rhs.getFullName(): return False
799 # If identical types and names, then go the whole hog and test children
800 # Could be sped up by testing property by property...
801 return self.getStrDescriptor() == rhs.getStrDescriptor()
802 def __ne__(self,rhs):
803 return (not self.__eq__(rhs))
804
805 __hash__ = object.__hash__
806
807 def __bool__ (self):
808 return True
809
810
811
812
814 __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
815 'Enable' : 1, 'ErrorMax' : 1, 'ErrorCounter' : 0, 'AuditAlgorithms' : 0, \
816 'AuditInitialize' : 0, 'AuditReinitialize' : 0, 'AuditExecute' : 0, \
817 'AuditFinalize' : 0 }
818
819 def __init__( self, name = Configurable.DefaultName ):
820 super( ConfigurableAlgorithm, self ).__init__( name )
821 name = self.getName()
822 self._jobOptName = name[ name.find('/')+1 : ] # strips class
823
824 def __deepcopy__( self, memo ):
825 return self # algorithms are always shared
826
827 def getHandle( self ):
828 try:
829 from GaudiPython.Bindings import iAlgorithm
830 except ImportError:
831 from gaudimodule import iAlgorithm
832 return iAlgorithm( self.getJobOptName() )
833
834 def getGaudiType( self ):
835 return 'Algorithm'
836
837 def getJobOptName( self ):
838 return self._jobOptName
839
840
841
843 __slots__ = { 'OutputLevel' : 0, \
844 'AuditServices' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
845
846 def __deepcopy__( self, memo ):
847 return self # services are always shared
848
849 def copyChild( self, child ):
850 # Copy private tools but all else is shared
851 if isinstance(child, ConfigurableAlgTool) and not child.isPublic():
852 return copy.deepcopy( child )
853 else:
854 return child
855
856 def getHandle( self ):
857 try:
858 from GaudiPython.Bindings import iService
859 except ImportError:
860 from gaudimodule import iService
861 return iService( self._name )
862
863 def getGaudiType( self ):
864 return 'Service'
865
866 def getGaudiHandle( self ):
867 return GaudiHandles.ServiceHandle( self.toStringProperty() )
868
869 def toStringProperty( self ):
870 # called on conversion to a string property for the jocat
871 return self.getName()
872
873
874
876 __slots__ = { '_jobOptName' : '', 'OutputLevel' : 0, \
877 'AuditTools' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
878
879 def __init__( self, name = Configurable.DefaultName ):
880 super( ConfigurableAlgTool, self ).__init__( name )
881 name = self.getName()
882 name = name[ name.find('/')+1 : ] # strips class, if any
883 self._jobOptName = name
884
885 def getHandle( self ):
886 # iAlgTool isn't useful, unless one knows for sure that the tool exists
887 try:
888 from GaudiPython.Bindings import iProperty
889 except ImportError:
890 from gaudimodule import iProperty
891 return iProperty( self.getJobOptName() )
892
893 def getGaudiType( self ):
894 return 'AlgTool'
895
896 def getGaudiHandle( self ):
897 if self.isPublic():
898 return GaudiHandles.PublicToolHandle( self.toStringProperty() )
899 else:
900 return GaudiHandles.PrivateToolHandle( self.toStringProperty() )
901
902 def getPrintTitle(self):
903 if self.isPublic():
904 pop = 'Public '
905 else:
906 pop = 'Private '
907 return pop + Configurable.getPrintTitle(self)
908
909 def setParent( self, parentName ):
910# print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
911# print "Calling stack:"
912# import traceback
913# traceback.print_stack()
914 # propagate parent to AlgTools in children
915 for c in self.getAllChildren():
916 if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
917
918 # update my own parent
919 self._jobOptName = parentName + '.' + self._jobOptName
920
921
922 def getParent( self ):
923 dot = self._jobOptName.rfind('.')
924 if dot != -1:
925 return self._jobOptName[:dot]
926 else:
927 return ""
928
929 def getDirectParent( self ):
930 parent = self.getParent()
931 dot = parent.rfind('.')
932 if dot != -1:
933 return parent[dot+1:]
934 else:
935 return parent
936
937 def hasParent( self, parent ):
938 return self._jobOptName.startswith( parent + '.' )
939
940 def getJobOptName( self ):
941 return self._jobOptName
942
943 def isPublic( self ):
944 return self.isInToolSvc()
945
946 def isInToolSvc( self ):
947 return self.getParent() == 'ToolSvc'
948
949 def toStringProperty( self ):
950 # called on conversion to a string property for the jocat
951 return self.getType() + '/' + self.getName()
952
953
954
956 __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
957 'Enable' : 1 }
958
959 def __init__( self, name = Configurable.DefaultName ):
960 super( ConfigurableAuditor, self ).__init__( name )
961 name = self.getName()
962 name = name[ name.find('/')+1 : ] # strips class, if any
963 self._jobOptName = name
964
965 def getHandle( self ):
966 # Auditor handles don't exist ... use iProperty
967 try:
968 from GaudiPython.Bindings import iProperty
969 except ImportError:
970 from gaudimodule import iProperty
971 return iProperty( self.getJobOptName() )
972
973 def getGaudiType( self ):
974 return 'Auditor'
975
976 def getJobOptName( self ):
977 return self._jobOptName
978
979 def toStringProperty( self ):
980 # called on conversion to a string property for the jocat
981 return self.getType() + '/' + self.getName()
982
983 pass # class ConfigurableAuditor
984
985
986
988 def __init__( self, name, **kwargs ):
989 super( ConfigurableAlgorithm, self ).__init__( name )
990 self._jobOptName = name
991
992 for n, v in kwargs.items():
993 setattr(self, n, v)
994
995 def getDlls( self ):
996 return None
997
998 def getHandle( self ):
999 return None
1000
1001
1002
1004 __slots__ = {}
1005
1006 def __init__( self, name = Configurable.DefaultName, **kwargs ):
1007 super( ConfigurableUser, self ).__init__( name )
1008 for n, v in kwargs.items():
1009 setattr(self, n, v)
1010
1011 def getGaudiType( self ):
1012 return 'User'
1013
1014 def getDlls( self ):
1015 return None
1016
1017 def getHandle( self ):
1018 return None
#define max(a, b)
Definition cfImp.cxx:41
this metaclass installs PropertyProxy descriptors for Gaudi properties
__init__(self, name=Configurable.DefaultName)
__init__(self, name=Configurable.DefaultName)
__init__(self, name=Configurable.DefaultName)
__exit__(self, exception_type, exception_value, traceback)
__init__(self, name=Configurable.DefaultName, **kwargs)
__init__(self, name=DefaultName)
overwriteChild(self, idx, newChild)
__iadd__(self, configs, descr=None, index=None)
__str__(self, indent=0, headerLastIndentUnit=indentUnit)
__new__(cls, *args, **kwargs)
copyChildAndSetParent(self, cfg, parent)
Definition index.py:1