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 )
or isinstance( classtree, 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 ):
652 def _sorted_repr_set(value):
653 """Helper to print sorted set representation"""
654 return "{" +
repr(
sorted(value))[1:-1] +
"}" if value
else "set()"
656 indentStr = indent*Configurable.indentUnit
661 if logging.WARNING <= log.level:
662 if not Configurable._printOnce:
663 Configurable._printOnce = 1
664 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
673 headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
676 rep = Configurable._printHeader( headerIndent, title )
682 rep += indentStr +
'|-<no properties>' + os.linesep
686 for p
in props.keys():
687 nameWidth=
max(nameWidth,len(p))
688 propNames =
sorted(props.keys())
692 prefix = indentStr +
'|-%-*s' % (nameWidth,p)
694 if log.isEnabledFor( logging.DEBUG ):
695 if v != Configurable.propertyNoValue:
696 address =
' @%11s' % hex(
id(v))
701 default = defs.get(p)
703 if not isinstance(v,MutableSequence)
and v == Configurable.propertyNoValue:
705 strVal =
repr(default)
709 if isinstance(v,Configurable):
710 vv = v.getGaudiHandle()
713 if isinstance(vv,(GaudiHandles.GaudiHandle,
714 GaudiHandles.GaudiHandleArray,
715 DataHandle.DataHandle)):
717 strDef =
repr(default.toStringProperty())
718 if strDef ==
repr(vv.toStringProperty()):
720 elif isinstance(vv, set):
721 strVal = _sorted_repr_set(vv)
722 strDef = _sorted_repr_set(default)
725 strDef =
repr(default)
727 line = prefix +
' = ' + strVal
729 if strDef
is not None:
731 if len(line) + len(strDef) > Configurable.printHeaderWidth:
732 line += os.linesep + indentStr +
'| ' + (len(prefix)-len(indentStr)-3)*
' '
733 line +=
' (default: %s)' % (strDef,)
735 rep += line + os.linesep
741 if isinstance(c, ConfigurableAlgorithm): childNotToSort.append(c)
742 else: childToSort.append(c)
744 childToSort.sort(key=
lambda x : x.getName())
745 for cfg
in childToSort + childNotToSort:
746 rep += cfg.__str__( indent + 1,
'|=' ) + os.linesep
749 rep += Configurable._printFooter( indentStr, title )
763 for key,val
in sorted(properties.items()):
764 if isinstance(val,GaudiHandles.PublicToolHandle)
or isinstance(val,GaudiHandles.PrivateToolHandle):
765 propstr += val.getFullName()
766 elif isinstance(val,Configurable):
767 propstr +=
"({0}:{1})".
format(key,val.getFlattenedProperties())
768 elif isinstance(val,GaudiHandles.PublicToolHandleArray)
or isinstance(val,GaudiHandles.PrivateToolHandleArray):
771 if isinstance(th,Configurable):
772 propstr +=
"({0}:{1}".
format(th.getFullName(), th.getFlattenedProperties())
774 propstr += th.getFullName()
776 propstr +=
"({0}:{1})".
format(key,
str(val))
783 if hasattr( self,
"_name" ):
794 if self
is rhs:
return True
796 if not rhs:
return False
798 if not isinstance(rhs,Configurable):
return False
800 if self.
getFullName() != rhs.getFullName():
return False
805 return (
not self.
__eq__(rhs))
807 __hash__ = object.__hash__
816 __slots__ = {
'_jobOptName' : 0,
'OutputLevel' : 0, \
817 'Enable' : 1,
'ErrorMax' : 1,
'ErrorCounter' : 0,
'AuditAlgorithms' : 0, \
818 'AuditInitialize' : 0,
'AuditReinitialize' : 0,
'AuditExecute' : 0, \
819 'AuditFinalize' : 0 }
821 def __init__( self, name = Configurable.DefaultName ):
822 super( ConfigurableAlgorithm, self ).
__init__( name )
831 from GaudiPython.Bindings
import iAlgorithm
833 from gaudimodule
import iAlgorithm
845 __slots__ = {
'OutputLevel' : 0, \
846 'AuditServices' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
853 if isinstance(child, ConfigurableAlgTool)
and not child.isPublic():
854 return copy.deepcopy( child )
860 from GaudiPython.Bindings
import iService
862 from gaudimodule
import iService
863 return iService( self.
_name )
878 __slots__ = {
'_jobOptName' :
'',
'OutputLevel' : 0, \
879 'AuditTools' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
881 def __init__( self, name = Configurable.DefaultName ):
882 super( ConfigurableAlgTool, self ).
__init__( name )
884 name = name[ name.find(
'/')+1 : ]
890 from GaudiPython.Bindings
import iProperty
892 from gaudimodule
import iProperty
909 return pop + Configurable.getPrintTitle(self)
918 if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
933 dot = parent.rfind(
'.')
935 return parent[dot+1:]
958 __slots__ = {
'_jobOptName' : 0,
'OutputLevel' : 0, \
961 def __init__( self, name = Configurable.DefaultName ):
962 super( ConfigurableAuditor, self ).
__init__( name )
964 name = name[ name.find(
'/')+1 : ]
970 from GaudiPython.Bindings
import iProperty
972 from gaudimodule
import iProperty
991 super( ConfigurableAlgorithm, self ).
__init__( name )
994 for n, v
in kwargs.items():
1008 def __init__( self, name = Configurable.DefaultName, **kwargs ):
1009 super( ConfigurableUser, self ).
__init__( name )
1010 for n, v
in kwargs.items():