7 import copy, os, weakref
8 from AthenaCommon
import ConfigurableMeta
11 import GaudiKernel.GaudiHandles
as GaudiHandles
12 import GaudiKernel.DataHandle
as DataHandle
13 from collections.abc
import MutableSequence
17 __author__ =
'Wim Lavrijsen (WLavrijsen@lbl.gov)'
19 __all__ = [
'Configurable',
20 'ConfigurableAlgorithm',
'ConfigurablePyAlgorithm',
21 'ConfigurableAlgTool',
22 'ConfigurableAuditor',
23 'ConfigurableService',
29 from AthenaCommon.Logging
import logging
30 log = logging.getLogger(
'Configurable' )
42 def __exit__(self, exception_type, exception_value, traceback):
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."""
61 propertyNoValue =
'<no value>'
66 _fInSetDefaults = 0x01
79 allConfigurables = weakref.WeakValueDictionary()
87 _useGlobalInstances =
True
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."""
97 name = kwargs[
'name' ]
98 elif 'name' in cls.__init__.__code__.co_varnames:
100 index =
list(cls.__init__.__code__.co_varnames).
index(
'name' )
103 name = args[ index - 1 ]
106 name = cls.__init__.__defaults__[ index - (len(args)+1) ]
111 except (IndexError,TypeError):
112 raise TypeError(
'no "name" argument while instantiating "%s"' % cls.__name__ )
116 if hasattr( cls,
'DefaultedName' ):
117 name = cls.DefaultedName
120 elif not name
or type(name)
is not str:
122 raise TypeError(
'could not retrieve name from %s.__init__ arguments' % cls.__name__ )
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 )
129 if 0 <= name.find(
'/' ):
130 raise NameError(
'"%s": type separator "/" no allowed in component name, '
131 'typename is derived from configurable instead' % name )
133 if cls._useGlobalInstances:
136 conf = cls.configurables[ name ]
139 for n,v
in kwargs.items():
141 setattr( conf, n, v )
142 except AttributeError
as originalAttributeError:
145 acceptableKeyWord =
False
148 def flat( classtree ):
149 if isinstance(classtree, (list, tuple)):
150 return [ j
for i
in classtree
for j
in flat( i ) ]
157 allclasses = flat( inspect.getclasstree( [ conf.__class__ ] ) )
158 for confklass
in allclasses:
162 if n
in confklass.__init__.__code__.co_varnames:
163 acceptableKeyWord =
True
165 except AttributeError:
170 if not acceptableKeyWord:
171 raise originalAttributeError
181 conf = object.__new__( cls )
188 cls.__init__( conf, *args, **kwargs )
191 if cls._useGlobalInstances:
192 cls.configurables[ name ] = conf
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
205 klass = self.__class__
208 if klass == Configurable:
209 raise TypeError(
"%s is an ABC and can not be instantiated" %
str(Configurable))
216 if hasattr( self.__class__,
'DefaultedName' ):
217 self.
_name = self.__class__.DefaultedName
243 for name, proxy
in self._properties.
items():
245 d[ name ] = proxy.__get__( self )
246 except AttributeError:
249 d[
'_Configurable__children' ] = self.
__children
250 d[
'_name' ] = self.
_name
251 if hasattr(self,
'_jobOptName'):
262 if '_fIsLocked' in dct:
263 if dct[
'_fIsLocked' ]:
267 del dct[
'_fIsLocked']
269 for (n, v)
in dct.items():
270 setattr( self, n, v )
286 newconf = object.__new__( self.__class__ )
289 for proxy
in self._properties.
values():
291 proxy.__set__( newconf, proxy.__get__( self ) )
292 except AttributeError:
301 def __iadd__( self, configs, descr = None, index = None ):
303 raise RuntimeError( f
"cannot add children to locked configurable {self.getJobOptName()}" )
305 if not type(configs)
in (list,tuple):
306 configs = ( configs, )
312 if not isinstance( cfg, Configurable ):
313 raise TypeError(
"'%s' is not a Configurable" %
str(cfg) )
318 ccjo = cc.getJobOptName()
320 if c.getJobOptName() == ccjo:
321 log.debug(
'attempt to add a duplicate (%s.%s) ... dupe ignored', joname
or self.
name(), ccjo )
331 descr.__set__( self, cc )
333 setattr( self, cc.getName(), cc )
334 except AttributeError:
341 return super( Configurable, self ).__getattribute__(attr)
343 if c.getName() == attr:
346 raise AttributeError(
"'%s' object has no attribute '%s'" % (self.__class__,attr) )
352 prop = self._properties[ attr ]
353 prop.__delete__( self )
354 prop.__set__( self, prop.default )
361 if c.getName() == attr:
366 del self.__dict__[ attr ]
367 except (AttributeError,KeyError):
371 if not isinstance(items, (list, tuple)):
382 log.error(
'not cloning %s, because new name given is same as old name', self.
getName() )
389 return copy.deepcopy( child )
403 if hasattr( cc,
'setParent' )
and parent:
404 cc.setParent( parent )
415 """Get all (private) configurable children, both explicit ones (added with +=)
416 and the ones in the private GaudiHandle properties"""
419 for proxy
in self._properties.
values():
421 c = proxy.__get__( self )
422 except AttributeError:
425 if isinstance(c,Configurable)
and not c.isPublic():
427 elif isinstance(c,GaudiHandles.GaudiHandle):
429 conf = c.configurable
430 except AttributeError:
433 if not conf.isPublic():
435 elif isinstance(c,GaudiHandles.GaudiHandleArray):
439 if isinstance(ci,Configurable):
443 conf = ci.configurable
444 except AttributeError:
456 elems.append( c.getFullName() )
461 if not hasattr(self,
'_fInitOk')
or not self.
_fInitOk:
463 raise TypeError(
"Configurable.__init__ not called in %s override" % self.__class__.__name__)
474 handle = self.getHandle()
476 log.debug(
'no handle for %s: not transporting properties', self.
_name )
480 for name
in self._properties.
keys():
481 if hasattr( self, name ):
482 setattr( handle, name, getattr(self,name) )
493 if isinstance( v, Configurable ):
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] )
517 for name, proxy
in self._properties.
items():
519 props[ name ] = proxy.__get__( self )
520 except AttributeError:
521 props[ name ] = Configurable.propertyNoValue
527 for name, proxy
in self._properties.
items():
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:
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
564 if name
in c.__dict__:
565 return c.__dict__[ name ]
569 v = cls._properties[name]
570 if hasattr( v,
'default' ):
603 if log.isEnabledFor( logging.DEBUG ):
612 return self._flags & self._fInSetDefaults
615 dlls = self.getDlls()
618 elif isinstance(dlls, str):
621 from AthenaCommon.AppMgr
import theApp
622 dlls =
filter(
lambda d: d
not in theApp.Dlls, dlls )
623 if dlls: theApp.Dlls += dlls
634 preLen = Configurable.printHeaderPre
635 postLen = Configurable.printHeaderWidth - preLen - 3 - len(title)
636 postLen =
max(preLen,postLen)
637 return indentStr +
'/%s %s %s' % (preLen*
'*',title,postLen*
'*')
641 preLen = Configurable.printHeaderPre
642 postLen = Configurable.printHeaderWidth - preLen - 12 - len(title)
643 postLen =
max(preLen,postLen)
644 return indentStr +
'\\%s (End of %s) %s' % (preLen*
'-',title,postLen*
'-')
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()"
654 indentStr = indent*Configurable.indentUnit
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
671 headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
674 rep = Configurable._printHeader( headerIndent, title )
680 rep += indentStr +
'|-<no properties>' + os.linesep
684 for p
in props.keys():
685 nameWidth=
max(nameWidth,len(p))
686 propNames =
sorted(props.keys())
690 prefix = indentStr +
'|-%-*s' % (nameWidth,p)
692 if log.isEnabledFor( logging.DEBUG ):
693 if v != Configurable.propertyNoValue:
694 address =
' @%11s' % hex(
id(v))
699 default = defs.get(p)
701 if not isinstance(v,MutableSequence)
and v == Configurable.propertyNoValue:
703 strVal =
repr(default)
707 if isinstance(v,Configurable):
708 vv = v.getGaudiHandle()
711 if isinstance(vv,(GaudiHandles.GaudiHandle,
712 GaudiHandles.GaudiHandleArray,
713 DataHandle.DataHandle)):
715 strDef =
repr(default.toStringProperty())
716 if strDef ==
repr(vv.toStringProperty()):
718 elif isinstance(vv, set):
719 strVal = _sorted_repr_set(vv)
720 strDef = _sorted_repr_set(default)
723 strDef =
repr(default)
725 line = prefix +
' = ' + strVal
727 if strDef
is not None:
729 if len(line) + len(strDef) > Configurable.printHeaderWidth:
730 line += os.linesep + indentStr +
'| ' + (len(prefix)-len(indentStr)-3)*
' '
731 line +=
' (default: %s)' % (strDef,)
733 rep += line + os.linesep
739 if isinstance(c, ConfigurableAlgorithm): childNotToSort.append(c)
740 else: childToSort.append(c)
742 childToSort.sort(key=
lambda x : x.getName())
743 for cfg
in childToSort + childNotToSort:
744 rep += cfg.__str__( indent + 1,
'|=' ) + os.linesep
747 rep += Configurable._printFooter( indentStr, title )
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)):
769 if isinstance(th,Configurable):
770 propstr +=
"({0}:{1}".
format(th.getFullName(), th.getFlattenedProperties())
772 propstr += th.getFullName()
774 propstr +=
"({0}:{1})".
format(key,
str(val))
781 if hasattr( self,
"_name" ):
792 if self
is rhs:
return True
794 if not rhs:
return False
796 if not isinstance(rhs,Configurable):
return False
798 if self.
getFullName() != rhs.getFullName():
return False
803 return (
not self.
__eq__(rhs))
805 __hash__ = object.__hash__
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 }
819 def __init__( self, name = Configurable.DefaultName ):
820 super( ConfigurableAlgorithm, self ).
__init__( name )
829 from GaudiPython.Bindings
import iAlgorithm
831 from gaudimodule
import iAlgorithm
843 __slots__ = {
'OutputLevel' : 0, \
844 'AuditServices' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
851 if isinstance(child, ConfigurableAlgTool)
and not child.isPublic():
852 return copy.deepcopy( child )
858 from GaudiPython.Bindings
import iService
860 from gaudimodule
import iService
861 return iService( self.
_name )
876 __slots__ = {
'_jobOptName' :
'',
'OutputLevel' : 0, \
877 'AuditTools' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
879 def __init__( self, name = Configurable.DefaultName ):
880 super( ConfigurableAlgTool, self ).
__init__( name )
882 name = name[ name.find(
'/')+1 : ]
888 from GaudiPython.Bindings
import iProperty
890 from gaudimodule
import iProperty
907 return pop + Configurable.getPrintTitle(self)
916 if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
931 dot = parent.rfind(
'.')
933 return parent[dot+1:]
956 __slots__ = {
'_jobOptName' : 0,
'OutputLevel' : 0, \
959 def __init__( self, name = Configurable.DefaultName ):
960 super( ConfigurableAuditor, self ).
__init__( name )
962 name = name[ name.find(
'/')+1 : ]
968 from GaudiPython.Bindings
import iProperty
970 from gaudimodule
import iProperty
989 super( ConfigurableAlgorithm, self ).
__init__( name )
992 for n, v
in kwargs.items():
1006 def __init__( self, name = Configurable.DefaultName, **kwargs ):
1007 super( ConfigurableUser, self ).
__init__( name )
1008 for n, v
in kwargs.items():