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 )
918 return getattr( obj, self.
__name__ )
921 object.__setattr__( obj, self.
__name__, value )
926 def __init__( self, name = Configurable.DefaultName ):
927 Configurable.__init__( self, name )
935 def getDlls( self ) :
pass
941 super( ConfigurableGeneric, self ).
__setattr__( name, value )
945 if isinstance( value, Configurable ):
946 self.__dict__[ name ] = value
958 class ConfigurableAlgorithm( Configurable ):
959 __slots__ = {
'_jobOptName' : 0,
'OutputLevel' : 0, \
960 'Enable' : 1,
'ErrorMax' : 1,
'ErrorCount' : 0,
'AuditAlgorithms' : 0, \
961 'AuditInitialize' : 0,
'AuditReinitialize' : 0,
'AuditExecute' : 0, \
962 'AuditFinalize' : 0,
'AuditBeginRun' : 0,
'AuditEndRun' : 0 }
964 def __init__( self, name = Configurable.DefaultName ):
965 super( ConfigurableAlgorithm, self ).
__init__( name )
983 __slots__ = {
'OutputLevel' : 0, \
984 'AuditServices' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
993 return iService( self.
_name )
1007 __slots__ = {
'_jobOptName' :
'',
'OutputLevel' : 0, \
1008 'AuditTools' : 0,
'AuditInitialize' : 0,
'AuditFinalize' : 0 }
1011 super( ConfigurableAlgTool, self ).
__init__( name )
1012 if '.' not in self.
_name:
1016 name = name[ name.find(
'/')+1 : ]
1037 return pop + Configurable.getPrintTitle(self)
1046 if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
1050 name = name[name.rfind(
'.')+1:]
1054 dot = self._jobOptName.rfind(
'.')
1061 return self._jobOptName.startswith( parent +
'.' )
1070 return self._jobOptName.startswith(
'ToolSvc.')
1081 name = name[name.rfind(
'.')+1:]
1082 return str( self.
getType() +
'/' + name )
1089 __slots__ = {
'_jobOptName' : 0,
'OutputLevel' : 0, \
1093 super( ConfigurableAuditor, self ).
__init__( name )
1095 name = name[ name.find(
'/')+1 : ]
1113 __slots__ = {
"__users__": [],
1114 "__used_instances__": [],
1124 __used_configurables__ = []
1127 __queried_configurables__ = []
1128 def __init__( self, name = Configurable.DefaultName, _enabled = True, **kwargs ):
1129 super( ConfigurableUser, self ).
__init__( name )
1130 for n, v
in kwargs.items():
1147 if type(used)
is tuple:
1148 used, used_name = used
1152 if type(used)
is str:
1153 used_class = confDbGetConfigurable(used)
1158 inst = used_class(name = used_name, _enabled =
False)
1159 except AttributeError:
1163 inst = used_class(name = used_name)
1167 if type(queried)
is str:
1168 queried = confDbGetConfigurable(used)
1169 inst = queried(_enabled =
False)
1170 except AttributeError:
1175 Declare that we are going to modify the Configurable 'other' in our
1176 __apply_configuration__.
1178 self.__used_instances__.append(other)
1179 if hasattr(other,
"__users__"):
1180 other.__users__.append(self)
1183 Declare that we are going to retrieve property values from the
1184 ConfigurableUser 'other' in our __apply_configuration__.
1186 if not isinstance(other, ConfigurableUser):
1187 raise Error(
"'%s': Cannot make passive use of '%s', it is not a ConfigurableUser" % (self.
name(), other.name()))
1188 other.__addActiveUseOf(self)
1198 Remove this ConfigurableUser instance from the users list of the used
1202 if hasattr(used,
"__users__"):
1203 used.__users__.remove(self)
1207 Propagate the property 'name' (if set) to other configurables (if possible).
1210 propagate to all the entries in __used_configurables__
1211 a configurable instance:
1212 propagate only to it
1213 list of configurable instances:
1214 propagate to all of them.
1218 - if the local property is set, the other property will be overwritten
1219 - local property not set and other set => keep other
1220 - local property not set and other not set => overwrite the default for
1221 ConfigurableUser instances and set the property for Configurables
1226 elif type(others)
not in [ list, tuple ] :
1232 for other
in [ o
for o
in others
if name
in o.__slots__ ]:
1235 if other.isPropertySet(name):
1236 log.warning(
"Property '%(prop)s' is set in both '%(self)s' and '%(other)s', using '%(self)s.%(prop)s'"%
1237 {
"self": self.
name(),
1238 "other": other.name(),
1240 other.setProp(name, value)
1242 elif not other.isPropertySet(name):
1243 if isinstance(other,ConfigurableUser):
1244 otherType =
type(other._properties[name].getDefault())
1245 other._properties[name].setDefault(value)
1246 if otherType
in [list, dict]:
1249 other.setProp(name, otherType(value))
1251 other.setProp(name, value)
1256 Call propagateProperty for each property listed in 'names'.
1257 If 'names' is None, all the properties are propagated.
1261 names = [ p
for p
in self.
__slots__ if not p.startswith(
"_") ]
1267 Function to be overridden to convert the high level configuration into a
1269 The default implementation calls applyConf, which is the method defined
1270 in some ConfigurableUser implementations.
1276 Function to be overridden to convert the high level configuration into a
1283 Function used to define the name of the private instance of a given class
1285 This method is used when the __used_configurables_property__ declares the
1286 need of a private used configurable without specifying the name.
1288 if type(cls)
is str:
1291 clName = cls.__name__
1292 return "%s_%s" % (self.name(), clName)
1296 Return the used instance with a given name.
1299 if i.name() == name:
1300 if hasattr(i,
"_enabled"):
1305 raise KeyError(name)
1308 postConfigActions = []
1311 Add a new callable ('function') to the list of post-configuration actions.
1312 If the callable is already in the list, it is moved to the end of the list.
1313 The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1316 postConfigActions.remove(function)
1319 postConfigActions.append(function)
1322 Remove a collable from the list of post-config actions.
1323 The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1325 postConfigActions.remove(function)
1327 _appliedConfigurableUsers_ =
False
1330 Call the apply method of all the ConfigurableUser instances respecting the
1331 dependencies. First the C.U.s that are not used by anybody, then the used
1332 ones, when they are not used anymore.
1335 global _appliedConfigurableUsers_, postConfigActions
1336 if _appliedConfigurableUsers_:
1338 _appliedConfigurableUsers_ =
True
1341 for c
in Configurable.allConfigurables.values()
1342 if hasattr(c,
"__apply_configuration__") ]
1344 while applied
and confUsers:
1348 if hasattr(c,
"__users__")
and c.__users__:
1349 newConfUsers.append(c)
1354 enabled = (
not hasattr(c,
"_enabled"))
or c._enabled
1356 log.info(
"applying configuration of %s", c.name())
1357 c.__apply_configuration__()
1360 log.info(
"skipping configuration of %s", c.name())
1361 if hasattr(c,
"__detach_used__"):
1364 confUsers = newConfUsers
1367 raise Error(
"Detected loop in the ConfigurableUser "
1368 " dependencies: %r" % [ c.name()
1369 for c
in confUsers ])
1372 unknown = set(Configurable.allConfigurables)
1376 log.debug(
'new configurable created automatically: %s', k)
1378 Configurable.allConfigurables[k].properties()
1382 for action
in postConfigActions:
1387 Function to select all and only the configurables that have to be used in
1388 GaudiPython.AppMgr constructor.
1389 This is needed because in Athena the implementation have to be different (the
1390 configuration is used in a different moment).
1393 for k, v
in Configurable.allConfigurables.items()
1394 if v.getGaudiType() !=
"User" ]
1398 Clean up all configurations and configurables.
1400 for c
in Configurable.allConfigurables.values():
1401 c.__class__.configurables.clear()
1402 Configurable.allConfigurables.clear()
1405 ConfigurableGeneric.configurables.clear()
1406 from ProcessJobOptions
import _included_files
1408 for file
in _included_files:
1409 dirname, basname = os.path.split(file)
1410 basname, ext = os.path.splitext(basname)
1411 if basname
in sys.modules:
1412 del sys.modules[basname]
1413 _included_files.clear()