5 import copy, string, types, os
6 from inspect
import isclass
9 VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL
14 __all__ = [
'Configurable',
15 'ConfigurableAlgorithm',
16 'ConfigurableAlgTool',
17 'ConfigurableAuditor',
18 'ConfigurableService',
20 'VERBOSE',
'DEBUG',
'INFO',
'WARNING',
'ERROR',
'FATAL',
21 'appendPostConfigAction',
'removePostConfigAction' ]
25 log = logging.getLogger(
'Configurable' )
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).
36 return os.path.expandvars(data)
37 elif typ
in [list, tuple]:
51 Error occurred in the configuration process.
56 class PropertyReference(object):
60 return "@%s"%self.
name
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()
71 raise NameError(
"name '%s' not found resolving '%s'"%(refname,self))
74 """This function allow transparent integration with
75 Configurable.getValuedProperties.
82 except AttributeError:
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."""
97 propertyNoValue =
'<no value>'
113 allConfigurables = {}
114 configurableServices = {}
117 _configurationLocked =
False
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."""
128 name = kwargs[
'name' ]
129 elif 'name' in cls.__init__.func_code.co_varnames:
131 index = list(cls.__init__.func_code.co_varnames).index(
'name' )
134 name = args[ index - 1 ]
137 name = cls.__init__.func_defaults[ index - (len(args)+1) ]
142 except (IndexError,TypeError):
143 raise TypeError(
'no "name" argument while instantiating "%s"' % cls.__name__ )
147 if hasattr(cls,
'DefaultedName' ) :
148 name = cls.DefaultedName
151 elif not name
or type(name) != str:
153 raise TypeError(
'could not retrieve name from %s.__init__ arguments' % cls.__name__ )
157 if issubclass( cls, ConfigurableAlgTool)
and '.' not in name :
158 name =
'ToolSvc.' + name
168 if name
in cls.configurables:
169 conf = cls.configurables[ name ]
171 cls.configurables[ conf.getType() ] = conf
173 for n,v
in kwargs.items():
176 if not cls._configurationLocked
and not "_enabled" in kwargs
and isinstance(conf, ConfigurableUser):
179 setattr(conf,
"_enabled",
True)
183 spos = name.find(
'/' )
186 ti_name =
"%s/%s" % (name,name)
187 if ti_name
in cls.configurables:
189 return cls.configurables[ ti_name ]
194 if i_name == name[spos+1:]
and i_name
in cls.configurables:
196 return cls.configurables[ i_name ]
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 ))
203 if conf.__class__
is ConfigurableGeneric :
207 newconf = object.__new__( cls )
208 cls.__init__( newconf, *args, **kwargs )
213 for n
in newconf.__slots__:
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
226 log.error(
'attempt to redefine type of "%s" (was: %s, new: %s)%s',
227 name, conf.__class__.__name__, cls.__name__, error_explanation )
232 for n,v
in kwargs.items():
237 conf = object.__new__(cls)
238 cls.__init__( conf, *args, **kwargs )
241 cls.configurables[ name ] = conf
243 for base
in cls.__bases__:
244 if base.__name__ ==
'ConfigurableService':
245 cls.configurableServices[ name ] = conf
248 cls.allConfigurables[ name ] = conf
256 klass = self.__class__
259 if klass == Configurable:
260 raise TypeError,
"%s is an ABC and can not be instantiated" % str(Configurable)
264 meths = {
'getDlls' : 1,
269 for meth, nArgs
in meths.items():
271 f = getattr( klass, meth ).im_func
272 except AttributeError:
273 raise NotImplementedError,
"%s is missing in class %s" % (meth,str(klass))
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)
287 if hasattr(self.__class__,
'DefaultedName' ) :
288 self.
_name = self.__class__.DefaultedName
306 for name, proxy
in self._properties.items():
308 dict[ name ] = proxy.__get__( self )
309 except AttributeError:
312 dict[
'_Configurable__children' ] = self.
__children
313 dict[
'_Configurable__tools' ] = self.
__tools
314 dict[
'_name' ] = self.
_name
322 for n, v
in dict.items():
335 newconf = object.__new__( self.__class__ )
336 self.__class__.__init__( newconf, self.
getName() )
338 for proxy
in self._properties.values():
340 proxy.__set__( newconf, proxy.__get__( self ) )
341 except AttributeError:
351 if not type(configs)
in (list,tuple):
352 configs = ( configs, )
358 if not isinstance( cfg, Configurable ):
359 raise TypeError(
"'%s' is not a Configurable" % str(cfg) )
364 ccjo = cc.getJobOptName()
366 if c.getJobOptName() == ccjo:
367 log.error(
'attempt to add a duplicate ... dupe ignored%s', error_explanation )
370 self.__children.append( cc )
374 descr.__set__( self, cc )
376 setattr( self, cc.getName(), cc )
377 except AttributeError:
387 if c.getName() == attr:
390 raise AttributeError(
"'%s' object has no attribute '%s'" % (self.__class__,attr) )
394 raise RuntimeError(
"%s: Configuration cannot be modified after the ApplicationMgr has been started."%self.
name())
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) )
405 prop = self._properties[ attr ]
406 prop.__delete__( self )
407 prop.__set__( self, prop.default )
417 if c.getName() == attr:
418 self.__children.remove( c )
422 del self.__dict__[ attr ]
423 except (AttributeError,KeyError):
431 if type(items) != list
and type(items) != tuple:
441 return copy.deepcopy( child )
455 if hasattr( cc,
'setParent' )
and parent:
457 cc.setParent( parent )
458 except RuntimeError, e:
460 log.error( str(e) +
'%s', error_explanation )
461 ccbd = cc.configurables[ cc.getJobOptName() ]
464 for proxy
in self._properties.values():
465 if proxy.history.has_key( cc ):
466 proxy.__set__( ccbd, proxy.__get__( cc ) )
476 return self.__tools.values()
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",
485 """Get all (private) configurable children, both explicit ones (added with +=)
486 and the ones in the private GaudiHandle properties"""
489 for proxy
in self._properties.values():
491 c = proxy.__get__( self )
492 except AttributeError:
495 if isinstance(c,Configurable)
and not c.isPublic():
497 elif isinstance(c,GaudiHandle):
499 conf = c.configurable
500 except AttributeError:
503 if not conf.isPublic():
505 elif isinstance(c,GaudiHandleArray):
509 if isinstance(ci,Configurable):
513 conf = ci.configurable
514 except AttributeError:
526 elems.append( c.getFullName() )
531 if not hasattr(self,
'_initok')
or not self.
_initok:
534 "Configurable.__init__ not called in %s override" % self.__class__.__name__
548 handle = self.getHandle()
550 log.debug(
'no handle for %s: not transporting properties', self.
_name )
554 for name
in self._properties.keys():
555 if hasattr( self, name ):
556 setattr( handle, name, getattr(self,name) )
563 for name, proxy
in self._properties.items():
565 props[ name ] = proxy.__get__( self )
566 except AttributeError:
567 props[ name ] = Configurable.propertyNoValue
573 for name, proxy
in self._properties.items():
575 value = proxy.__get__( self )
576 if hasattr(value,
'getFullName') :
577 value = value.getFullName()
578 elif type(value)
in [list, tuple]:
581 if hasattr(i,
'getFullName'):
582 new_value.append(i.getFullName())
585 value =
type(value)(new_value)
586 elif type(value)
is dict:
589 if hasattr(value[i],
'getFullName'):
592 new_value[i] = value[i]
594 props[ name ] = value
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
626 if name
in c.__dict__:
627 return c.__dict__[ name ]
631 v = cls._properties[name]
632 if hasattr( v,
'default' ):
640 """Returns the value of the given property.
642 if hasattr(self, name):
643 return getattr(self, name)
648 """Set the value of a given property
650 return setattr(self, name, value)
653 """Tell if the property 'name' has been set or not.
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
659 if not hasattr(self, name):
664 if isinstance(default, (list, dict)):
665 value = getattr(self, name)
666 return value != default
688 log.error(
"jobOptName() is deprecated, use getJobOptName() instead for consistency%s",
702 if log.isEnabledFor( logging.DEBUG ):
710 def clone( self, name = None, **kwargs ) :
712 if hasattr(self,
'DefaultedName' ) : name = self.DefaultedName
713 else : name = self.getType()
715 newconf = Configurable.__new__( self.__class__, name )
716 self.__class__.__init__( newconf, name )
718 for proxy
in self._properties.values():
720 value = proxy.__get__( self )
721 if type(value)
in [ str, list, dict, tuple ]:
723 value =
type(value)(value)
724 proxy.__set__( newconf, value )
725 except AttributeError:
728 for c
in self.__children:
731 for n , t
in self.__tools.items():
732 newconf.addTool(t, n)
734 for name, value
in kwargs.items():
735 setattr(newconf, name, value)
741 dot = fullname.find(
'.')
743 parentname = fullname[:dot]
744 longname = fullname[dot+1:]
748 dot = longname.find(
'.')
750 name = longname[:dot]
753 return parentname, name, longname
756 if isclass(tool)
and issubclass(tool, ConfigurableAlgTool):
759 priv_tool = tool( self.
getName()+
'.' + name )
760 elif isinstance(tool, ConfigurableAlgTool):
762 name = tool.splitName()[1]
763 priv_tool = tool.clone( self.
getName()+
'.' + name )
766 classname = tool.__name__
768 classname =
type(tool).__name__
769 raise TypeError,
"addTool requires AlgTool configurable. Got %s type" % classname
773 setattr(self,name,self.
__tools[name])
787 handle = __main__.Service( svc )
792 if hasattr( self,
'configure' + svc ):
793 eval(
'self.configure' + svc +
'( handle )' )
796 dlls = self.getDlls()
799 elif type(dlls) == types.StringType:
802 from __main__
import theApp
803 dlls = filter(
lambda d: d
not in theApp.Dlls, dlls )
804 if dlls: theApp.Dlls += dlls
815 preLen = Configurable.printHeaderPre
816 postLen = Configurable.printHeaderWidth - preLen - 3 - len(title)
817 postLen = max(preLen,postLen)
818 return indentStr +
'/%s %s %s' % (preLen*
'*',title,postLen*
'*')
822 preLen = Configurable.printHeaderPre
823 postLen = Configurable.printHeaderWidth - preLen - 12 - len(title)
824 postLen = max(preLen,postLen)
825 return indentStr +
'\\%s (End of %s) %s' % (preLen*
'-',title,postLen*
'-')
830 def __str__( self, indent = 0, headerLastIndentUnit=indentUnit ):
832 indentStr = indent*Configurable.indentUnit
837 headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
840 rep = Configurable._printHeader( headerIndent, title )
846 rep += indentStr +
'|-<no properties>' + os.linesep
850 for p
in props.keys():
851 nameWidth=max(nameWidth,len(p))
852 for p, v
in props.items():
854 prefix = indentStr +
'|-%-*s' % (nameWidth,p)
856 if log.isEnabledFor( logging.DEBUG ):
857 if v != Configurable.propertyNoValue:
858 address =
' @%11s' % hex(id(v))
863 default = defs.get(p)
864 if v == Configurable.propertyNoValue:
866 strVal = repr(default)
870 if hasattr(v,
"getGaudiHandle"):
871 vv = v.getGaudiHandle()
874 if isinstance(vv,GaudiHandle)
or isinstance(vv,GaudiHandleArray):
876 if hasattr(default,
"toStringProperty"):
877 strDef = repr(default.toStringProperty())
879 strDef = repr(default)
880 if strDef == repr(vv.toStringProperty()):
884 strDef = repr(default)
886 line = prefix +
' = ' + strVal
888 if strDef
is not None:
890 if len(line) + len(strDef) > Configurable.printHeaderWidth:
891 line += os.linesep + indentStr +
'| ' + (len(prefix)-len(indentStr)-3)*
' '
892 line +=
' (default: %s)' % (strDef,)
894 rep += line + os.linesep
906 rep += cfg.__str__( indent + 1,
'|=' ) + os.linesep
909 rep += Configurable._printFooter( indentStr, title )
914 Return True is the instance can be "applied".
915 Always False for plain Configurable instances
916 (i.e. not ConfigurableUser).
926 return getattr( obj, self.
__name__ )
929 object.__setattr__( obj, self.
__name__, value )
934 def __init__( self, name = Configurable.DefaultName ):
935 Configurable.__init__( self, name )
943 def getDlls( self ) :
pass
949 super( ConfigurableGeneric, self ).
__setattr__( name, value )
953 if isinstance( value, Configurable ):
954 self.__dict__[ name ] = value
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 }
972 def __init__( self, name = Configurable.DefaultName ):
973 super( ConfigurableAlgorithm, self ).
__init__( name )
991 __slots__ = {
'OutputLevel' : 0, \
992 'AuditServices' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
1001 return iService( self.
_name )
1015 __slots__ = {
'_jobOptName' :
'',
'OutputLevel' : 0, \
1016 'AuditTools' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
1019 super( ConfigurableAlgTool, self ).
__init__( name )
1020 if '.' not in self.
_name:
1024 name = name[ name.find(
'/')+1 : ]
1045 return pop + Configurable.getPrintTitle(self)
1054 if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
1058 name = name[name.rfind(
'.')+1:]
1062 dot = self._jobOptName.rfind(
'.')
1069 return self._jobOptName.startswith( parent +
'.' )
1078 return self._jobOptName.startswith(
'ToolSvc.')
1089 name = name[name.rfind(
'.')+1:]
1090 return str( self.
getType() +
'/' + name )
1097 __slots__ = {
'_jobOptName' : 0,
'OutputLevel' : 0, \
1101 super( ConfigurableAuditor, self ).
__init__( name )
1103 name = name[ name.find(
'/')+1 : ]
1121 __slots__ = {
"__users__": [],
1122 "__used_instances__": [],
1133 __used_configurables__ = []
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():
1157 if type(used)
is tuple:
1158 used, used_name = used
1162 if type(used)
is str:
1163 used_class = confDbGetConfigurable(used)
1168 inst = used_class(name = used_name, _enabled =
False)
1169 except AttributeError:
1173 inst = used_class(name = used_name)
1177 if type(queried)
is str:
1178 queried = confDbGetConfigurable(used)
1179 inst = queried(_enabled =
False)
1180 except AttributeError:
1185 Declare that we are going to modify the Configurable 'other' in our
1186 __apply_configuration__.
1188 self.__used_instances__.append(other)
1189 if hasattr(other,
"__users__"):
1190 other.__users__.append(self)
1193 Declare that we are going to retrieve property values from the
1194 ConfigurableUser 'other' in our __apply_configuration__.
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)
1208 Remove this ConfigurableUser instance from the users list of the used
1212 if hasattr(used,
"__users__"):
1213 used.__users__.remove(self)
1217 Propagate the property 'name' (if set) to other configurables (if possible).
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.
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
1236 elif type(others)
not in [ list, tuple ] :
1242 for other
in [ o
for o
in others
if name
in o.__slots__ ]:
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(),
1250 other.setProp(name, value)
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]:
1259 other.setProp(name, otherType(value))
1261 other.setProp(name, value)
1266 Call propagateProperty for each property listed in 'names'.
1267 If 'names' is None, all the properties are propagated.
1271 names = [ p
for p
in self.
__slots__ if not p.startswith(
"_") ]
1277 Function to be overridden to convert the high level configuration into a
1279 The default implementation calls applyConf, which is the method defined
1280 in some ConfigurableUser implementations.
1286 Function to be overridden to convert the high level configuration into a
1293 Function used to define the name of the private instance of a given class
1295 This method is used when the __used_configurables_property__ declares the
1296 need of a private used configurable without specifying the name.
1298 if type(cls)
is str:
1301 clName = cls.__name__
1302 return "%s_%s" % (self.name(), clName)
1306 Return the used instance with a given name.
1309 if i.name() == name:
1310 if hasattr(i,
"_enabled"):
1315 raise KeyError(name)
1319 Return True is the instance can be "applied".
1325 postConfigActions = []
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'.
1333 postConfigActions.remove(function)
1336 postConfigActions.append(function)
1339 Remove a callable from the list of post-config actions.
1340 The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1342 postConfigActions.remove(function)
1344 _appliedConfigurableUsers_ =
False
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.
1352 global _appliedConfigurableUsers_, postConfigActions
1353 if _appliedConfigurableUsers_:
1355 _appliedConfigurableUsers_ =
True
1357 def applicableConfUsers():
1359 Generator returning all the configurables that can be applied in the
1360 order in which they can be applied.
1372 yield (c
for c
in Configurable.allConfigurables.values()
1373 if c.isApplicable()).next()
1375 debugApplyOrder =
'GAUDI_DUBUG_CONF_USER' in os.environ
1376 for c
in applicableConfUsers():
1378 log.info(
"applying configuration of %s", c.name())
1380 sys.stderr.write(
'applying %r' % c)
1381 c.__apply_configuration__()
1384 log.info(
"skipping configuration of %s", c.name())
1386 if hasattr(c,
"__detach_used__"):
1391 leftConfUsers = [c
for c
in Configurable.allConfigurables.values()
1392 if hasattr(c,
'__apply_configuration__')
and
1393 c._enabled
and not c._applied]
1396 raise Error(
"Detected loop in the ConfigurableUser"
1397 " dependencies: %r" % [ c.name()
1398 for c
in leftConfUsers ])
1401 unknown = set(Configurable.allConfigurables)
1405 log.debug(
'new configurable created automatically: %s', k)
1407 Configurable.allConfigurables[k].properties()
1411 for action
in postConfigActions:
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.
1420 @see applyConfigurableUsers()
1423 global _appliedConfigurableUsers_, postConfigActions
1424 if _appliedConfigurableUsers_:
1426 _appliedConfigurableUsers_ =
True
1428 debugApplyOrder =
'GAUDI_DUBUG_CONF_USER' in os.environ
1430 for c
in Configurable.allConfigurables.values()
1431 if hasattr(c,
"__apply_configuration__") ]
1433 while applied
and confUsers:
1437 if hasattr(c,
"__users__")
and c.__users__:
1438 newConfUsers.append(c)
1443 enabled = (
not hasattr(c,
"_enabled"))
or c._enabled
1445 log.info(
"applying configuration of %s", c.name())
1447 sys.stderr.write(
'applying %r' % c)
1448 c.__apply_configuration__()
1451 log.info(
"skipping configuration of %s", c.name())
1452 if hasattr(c,
"__detach_used__"):
1455 confUsers = newConfUsers
1458 raise Error(
"Detected loop in the ConfigurableUser "
1459 " dependencies: %r" % [ c.name()
1460 for c
in confUsers ])
1463 unknown = set(Configurable.allConfigurables)
1467 log.debug(
'new configurable created automatically: %s', k)
1469 Configurable.allConfigurables[k].properties()
1473 for action
in postConfigActions:
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).
1484 for k, v
in Configurable.allConfigurables.items()
1485 if v.getGaudiType() !=
"User" ]
1489 Clean up all configurations and configurables.
1491 for c
in Configurable.allConfigurables.values():
1492 c.__class__.configurables.clear()
1493 Configurable.allConfigurables.clear()
1496 ConfigurableGeneric.configurables.clear()
1497 from ProcessJobOptions
import _included_files
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()