Gaudi Framework, version v23r5

Home   Generated: Wed Nov 28 2012
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Configurable.py
Go to the documentation of this file.
1 # File: AthenaCommon/python/Configurable.py
2 # Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
3 # Author: Martin Woudstra (Martin.Woudstra@cern.ch)
4 
5 import copy, string, types, os
6 from inspect import isclass
7 import GaudiKernel.ConfigurableMeta as ConfigurableMeta
8 from GaudiKernel.Constants import error_explanation, \
9  VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL
10 from GaudiKernel.PropertyProxy import PropertyProxy
11 from GaudiKernel.GaudiHandles import *
12 
13 ### data ---------------------------------------------------------------------
14 __all__ = [ 'Configurable',
15  'ConfigurableAlgorithm',
16  'ConfigurableAlgTool',
17  'ConfigurableAuditor',
18  'ConfigurableService',
19  'ConfigurableUser',
20  'VERBOSE','DEBUG','INFO', 'WARNING', 'ERROR', 'FATAL',
21  'appendPostConfigAction', 'removePostConfigAction' ]
22 
23 ## for messaging
24 import logging
25 log = logging.getLogger( 'Configurable' )
26 
27 def expandvars(data):
28  """
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).
32  """
33  import os.path
34  typ = type(data)
35  if typ is str:
36  return os.path.expandvars(data)
37  elif typ in [list, tuple]:
38  collect = []
39  for i in data:
40  collect.append(expandvars(i))
41  return typ(collect)
42  elif typ is dict:
43  collect = {}
44  for k in data:
45  collect[expandvars(k)] = expandvars(data[k])
46  return collect
47  return data
48 
49 class Error(RuntimeError):
50  """
51  Error occurred in the configuration process.
52  """
53  pass
54 
55 ## Allow references to options as in old style
56 class PropertyReference(object):
57  def __init__(self,propname):
58  self.name = propname
59  def __repr__(self):
60  return "@%s"%self.name
61  def __resolve__(self):
62  # late binding for property references
63  retval = None
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()
70  else:
71  raise NameError("name '%s' not found resolving '%s'"%(refname,self))
72  return retval
73  def getFullName(self):
74  """This function allow transparent integration with
75  Configurable.getValuedProperties.
76  """
77  try:
78  return self.__resolve__()
79  except NameError:
80  # ignore the error if we cannot resolve the name yet
81  return self
82  except AttributeError:
83  # ignore the error if we cannot resolve the attribute yet
84  return self
85 
86 ### base class for configurable Gaudi algorithms/services/algtools/etc. ======
87 class Configurable( object ):
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."""
92 
93  ## for detecting the default name
94  class DefaultName:
95  pass
96 
97  propertyNoValue = '<no value>'
98  indentUnit = '| '
99  printHeaderWidth=100
100  printHeaderPre=5
101 
103 
104  __slots__ = (
105  '__children', # controlled components, e.g. private AlgTools
106  '__tools', # private AlgTools (#PM-->)
107  '_name', # the (unqualified) component name
108  '_inSetDefaults', # currently setting default values
109  '_initok', # used to enforce base class init
110  '_setupok' # for debugging purposes (temporary)
111  )
112 
113  allConfigurables = {} # just names would do, but currently refs to the actual
114  configurableServices = {} # just names would do, but currently refs to the actual
115  # configurables is needed for (temporary) backwards
116  # compatibility; will change in the future
117  _configurationLocked = False
118 
119  def __new__ ( cls, *args, **kwargs ):
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."""
123 
124  global log
125  # try to get the name of the Configurable (having a name is compulsory)
126  if 'name' in kwargs:
127  # simple keyword (by far the easiest)
128  name = kwargs[ 'name' ]
129  elif 'name' in cls.__init__.func_code.co_varnames:
130  # either positional in args, or default
131  index = list(cls.__init__.func_code.co_varnames).index( 'name' )
132  try:
133  # var names index is offset by one as __init__ is to be called with self
134  name = args[ index - 1 ]
135  except IndexError:
136  # retrieve default value, then
137  name = cls.__init__.func_defaults[ index - (len(args)+1) ]
138  else:
139  # positional index is assumed (will work most of the time)
140  try:
141  name = args[1] # '0' is for self
142  except (IndexError,TypeError):
143  raise TypeError( 'no "name" argument while instantiating "%s"' % cls.__name__ )
144 
145  argname = name
146  if name == Configurable.DefaultName :
147  if hasattr(cls, 'DefaultedName' ) :
148  name = cls.DefaultedName
149  else :
150  name = cls.getType()
151  elif not name or type(name) != str:
152  # unnamed, highly specialized user code, etc. ... unacceptable
153  raise TypeError( 'could not retrieve name from %s.__init__ arguments' % cls.__name__ )
154 
155  # Handle the case of global tools to prepend ToolSvc in the name.
156  # This is needed for compatibility with old JobOptions files being read
157  if issubclass( cls, ConfigurableAlgTool) and '.' not in name :
158  name = 'ToolSvc.' + name
159 
160  # close backdoor access to otherwise private subalgs/tools
161  #PM if 0 <= name.find( '.' ):
162  #PM # temp protection for old style types
163  #PM from OldStyleConfig import GenericConfigurable
164  #PM if not issubclass( cls, GenericConfigurable ): # except raised for new types only
165  #PM raise NameError( '"%s": backdoor access to private configurables not allowed' % name )
166 
167  # ordinary recycle case
168  if name in cls.configurables:
169  conf = cls.configurables[ name ]
170  if name != argname: # special case: user derived <-> real ... make same
171  cls.configurables[ conf.getType() ] = conf
172  #---PM: Initialize additional properties
173  for n,v in kwargs.items():
174  if n != "name": # it should not be confused with a normal property
175  setattr(conf, n, v)
176  if not cls._configurationLocked and not "_enabled" in kwargs and isinstance(conf, ConfigurableUser):
177  # Ensure that the ConfigurableUser gets enabled if nothing is
178  # specified in the constructor.
179  setattr(conf, "_enabled", True)
180  return conf
181 
182  # a couple of special cases (note that these cases don't mix)
183  spos = name.find( '/' )
184  ti_name = None
185  if spos < 0:
186  ti_name = "%s/%s" % (name,name)
187  if ti_name in cls.configurables:
188  # support for old-style name as type/name lookup where name==type
189  return cls.configurables[ ti_name ]
190 
191  i_name = None
192  if spos > 0:
193  i_name = name[:spos]
194  if i_name == name[spos+1:] and i_name in cls.configurables:
195  # this is the opposite of the above special case
196  return cls.configurables[ i_name ]
197 
198  # the following is purely for debugging support and should realistically bomb
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 ))
202  if conf: # wrong type used?
203  if conf.__class__ is ConfigurableGeneric :
204  # If the instance found is ConfigurableGeneric then
205  # we create a new one with the proper type and fill with
206  # the contents of the generic one
207  newconf = object.__new__( cls )
208  cls.__init__( newconf, *args, **kwargs )
209  # initialize with the properties of generic configurable
210  # (we map the names of the properties to lowercase versions because
211  # old options are not case sensitive)
212  names = {}
213  for n in newconf.__slots__:
214  names[n.lower()] = n
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
223  return newconf
224  else :
225  # will be an actual error in the future (now only report as such)
226  log.error( 'attempt to redefine type of "%s" (was: %s, new: %s)%s',
227  name, conf.__class__.__name__, cls.__name__, error_explanation )
228  # in the future:
229  # return None # will bomb on use (or go unharmed on non-use)
230  # for now, allow use through allConfigurables lookup
231  #---PM: Initialize additional properties
232  for n,v in kwargs.items():
233  setattr(conf, n, v)
234  return conf
235 
236  # still here: create a new instance and initialize it
237  conf = object.__new__(cls)
238  cls.__init__( conf, *args, **kwargs )
239 
240  # update normal, per-class cache
241  cls.configurables[ name ] = conf
242 
243  for base in cls.__bases__:
244  if base.__name__ == 'ConfigurableService':
245  cls.configurableServices[ name ] = conf
246 
247  # update generics super-cache, if needed
248  cls.allConfigurables[ name ] = conf
249  #-->PM#if hasattr( cls, 'getType' ) and name.find('/') < 0:
250  #-->PM# cls.allConfigurables[ cls.getType() + '/' + name ] = conf
251 
252  return conf
253 
254  def __init__( self, name = DefaultName ):
255  # check class readiness, all required overloads should be there now
256  klass = self.__class__
257 
258  # this is an abstract class
259  if klass == Configurable:
260  raise TypeError, "%s is an ABC and can not be instantiated" % str(Configurable)
261 
262  # the following methods require overloading
263  #NOT YET meths = { 'getServices' : 1, # retrieve list of services to configure
264  meths = { 'getDlls' : 1, # provide list of Dlls to load
265  'getGaudiType' : 1, # return string describing component class
266  'getHandle' : 1 } # provide access to C++ side component instance
267 # 'getType' : 1 } # return the type of the actual C++ component
268 
269  for meth, nArgs in meths.items():
270  try:
271  f = getattr( klass, meth ).im_func
272  except AttributeError:
273  raise NotImplementedError, "%s is missing in class %s" % (meth,str(klass))
274 
275  # in addition, verify the number of arguments w/o defaults
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)
280 
281  # for using this Configurable as a (Gaudi) sequence
282  self.__children = []
283  self.__tools = {}
284 
285  # know who we are
286  if name == Configurable.DefaultName :
287  if hasattr(self.__class__, 'DefaultedName' ) :
288  self._name = self.__class__.DefaultedName
289  else :
290  self._name = self.getType()
291  else :
292  self._name = name
293 
294  # set to True when collecting defaults, False otherwise
295  self._inSetDefaults = False
296 
297  # for later, in case __init__ itself is overridden
298  self._initok = True
299 
300  # for debugging purposes (temporary)
301  self._setupok = False
302 
303  # pickle support
304  def __getstate__ (self):
305  dict = {}
306  for name, proxy in self._properties.items():
307  try:
308  dict[ name ] = proxy.__get__( self )
309  except AttributeError:
310  pass
311 
312  dict[ '_Configurable__children' ] = self.__children
313  dict[ '_Configurable__tools' ] = self.__tools
314  dict[ '_name' ] = self._name
315  return dict
316 
317  def __getnewargs__(self) :
318  return (self._name,)
319 
320  def __setstate__ ( self, dict ):
321  self._initok = True
322  for n, v in dict.items():
323  setattr (self, n, v)
324  return
325 
326  # to allow a few basic sanity checks, as well as nice syntax
327  def __len__( self ):
328  return len( self.__children )
329 
330  def __iter__( self ):
331  return iter( self.__children )
332 
333  # ownership rules of self through copying
334  def __deepcopy__( self, memo ):
335  newconf = object.__new__( self.__class__ )
336  self.__class__.__init__( newconf, self.getName() )
337 
338  for proxy in self._properties.values():
339  try:
340  proxy.__set__( newconf, proxy.__get__( self ) )
341  except AttributeError:
342  pass # means property was not set for self
343 
344  for c in self.__children:
345  newconf += c # processes proper copy semantics
346 
347  return newconf
348 
349  # hierarchy building, and ownership rules of children
350  def __iadd__( self, configs, descr = None ):
351  if not type(configs) in (list,tuple):
352  configs = ( configs, )
353 
354  joname = self.getJobOptName()
355 
356  for cfg in configs:
357  # prevent type mismatches
358  if not isinstance( cfg, Configurable ):
359  raise TypeError( "'%s' is not a Configurable" % str(cfg) )
360 
361  cc = self.copyChildAndSetParent( cfg, joname )
362 
363  # filters dupes; usually "ok" (backdoor should catch them)
364  ccjo = cc.getJobOptName()
365  for c in self.__children:
366  if c.getJobOptName() == ccjo:
367  log.error( 'attempt to add a duplicate ... dupe ignored%s', error_explanation )
368  break
369  else:
370  self.__children.append( cc )
371 
372  try:
373  if descr: # support for tool properties
374  descr.__set__( self, cc )
375  else:
376  setattr( self, cc.getName(), cc )
377  except AttributeError:
378  pass # to allow free addition of tools/subalgorithms
379 
380  return self
381 
382  def __getattr__( self, attr ): # until ToolProperties exist ...
383 
384  if attr in self.__tools : return self.__tools[attr]
385 
386  for c in self.__children:
387  if c.getName() == attr:
388  return c
389 
390  raise AttributeError( "'%s' object has no attribute '%s'" % (self.__class__,attr) )
391 
392  def __setattr__( self, name, value ) :
393  if self._configurationLocked:
394  raise RuntimeError("%s: Configuration cannot be modified after the ApplicationMgr has been started."%self.name())
395  try :
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) )
400 
401  def __delattr__( self, attr ):
402  # remove as property, otherwise try as child
403  try:
404  # remove history etc., then reset to default (in case set before)
405  prop = self._properties[ attr ]
406  prop.__delete__( self )
407  prop.__set__( self, prop.default )
408  return # reaches here? was property: done now
409  except KeyError:
410  pass
411  # otherwise, remove the private tool
412  if attr in self.__tools :
413  del self.__tools[attr]
414 
415  # otherwise, remove child, if one is so named
416  for c in self.__children:
417  if c.getName() == attr:
418  self.__children.remove( c )
419 
420  # potentially, there are left over caches (certain user derived classes)
421  try:
422  del self.__dict__[ attr ]
423  except (AttributeError,KeyError):
424  pass
425 
426  def __nonzero__(self):
427  return True
428 
429 
430  def remove( self, items ):
431  if type(items) != list and type(items) != tuple:
432  items = [ items ]
433 
434  self.__children = [ e for e in self.__children if not e in items ]
435 
436  def removeAll( self ):
437  self.remove( self.__children )
438 
439  # called by __iadd__; determines child copy semantics
440  def copyChild( self, child ):
441  return copy.deepcopy( child )
442 
443  def setParent( self, parentName ):
444  pass
445 
446  def getParent( self ):
447  return ""
448 
449  def hasParent( self, parent ):
450  return False
451 
452  def copyChildAndSetParent(self,cfg,parent):
453  cc = self.copyChild( cfg )
454 
455  if hasattr( cc, 'setParent' ) and parent:
456  try:
457  cc.setParent( parent )
458  except RuntimeError, e:
459  # temporary backdoor resolution for compatibility
460  log.error( str(e) + '%s', error_explanation )
461  ccbd = cc.configurables[ cc.getJobOptName() ]
462 
463  # merge properties, new over pre-existing
464  for proxy in self._properties.values():
465  if proxy.history.has_key( cc ):
466  proxy.__set__( ccbd, proxy.__get__( cc ) )
467 
468  # consolidate
469  cc = ccbd
470  return cc
471 
472  def getChildren( self ):
473  return self.__children[:] # read only
474 
475  def getTools( self ):
476  return self.__tools.values() # read only
477 
478  def children( self ):
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",
481  error_explanation )
482  return self.__children # by ref, for compatibility
483 
484  def getAllChildren( self ):
485  """Get all (private) configurable children, both explicit ones (added with +=)
486  and the ones in the private GaudiHandle properties"""
487  childs = []
488  # add private configurable properties (also inside handles)
489  for proxy in self._properties.values():
490  try:
491  c = proxy.__get__( self )
492  except AttributeError:
493  pass
494  else:
495  if isinstance(c,Configurable) and not c.isPublic():
496  childs.append(c)
497  elif isinstance(c,GaudiHandle):
498  try:
499  conf = c.configurable
500  except AttributeError:
501  pass
502  else:
503  if not conf.isPublic():
504  childs.append(conf)
505  elif isinstance(c,GaudiHandleArray):
506  # only setup private arrays
507  if not c.isPublic():
508  for ci in c:
509  if isinstance(ci,Configurable):
510  childs.append(ci)
511  else:
512  try:
513  conf = ci.configurable
514  except AttributeError:
515  pass
516  else:
517  childs.append(conf)
518 
519  # add explicit children
520  childs += self.__children
521  return childs
522 
523  def getSequence( self ):
524  elems = []
525  for c in self.__children:
526  elems.append( c.getFullName() )
527  return elems
528 
529  def setup( self ):
530  # make sure base class init has been called
531  if not hasattr(self,'_initok') or not self._initok:
532  # could check more, but this is the only explanation
533  raise TypeError, \
534  "Configurable.__init__ not called in %s override" % self.__class__.__name__
535 
536 # log.debug("calling setup() on " + self.getFullJobOptName())
537 
538  # setup self: this collects all values on the python side
539  self.__setupServices()
540  self.__setupDlls()
541  self.__setupDefaults()
542 
543  # setup children
544  for c in self.getAllChildren():
545  c.setup()
546 
547  # now get handle to work with for moving properties into the catalogue
548  handle = self.getHandle()
549  if not handle:
550  log.debug( 'no handle for %s: not transporting properties', self._name )
551  return # allowed, done early
552 
553  # pass final set of properties on to handle on the C++ side or JobOptSvc
554  for name in self._properties.keys():
555  if hasattr( self, name ): # means property has python-side value/default
556  setattr( handle, name, getattr(self,name) )
557 
558  # for debugging purposes
559  self._setupok = True
560 
561  def getProperties( self ):
562  props = {}
563  for name, proxy in self._properties.items():
564  try:
565  props[ name ] = proxy.__get__( self )
566  except AttributeError:
567  props[ name ] = Configurable.propertyNoValue
568 
569  return props
570 
571  def getValuedProperties( self ):
572  props = {}
573  for name, proxy in self._properties.items():
574  if self.isPropertySet(name):
575  value = proxy.__get__( self )
576  if hasattr(value, 'getFullName') :
577  value = value.getFullName()
578  elif type(value) in [list, tuple]:
579  new_value = []
580  for i in value:
581  if hasattr(i, 'getFullName'):
582  new_value.append(i.getFullName())
583  else:
584  new_value.append(i)
585  value = type(value)(new_value)
586  elif type(value) is dict:
587  new_value = {}
588  for i in value:
589  if hasattr(value[i], 'getFullName'):
590  new_value[i] = value[i].getFullName()
591  else:
592  new_value[i] = value[i]
593  value = new_value
594  props[ name ] = value
595 
596  return props
597 
598  def properties( self ):
599  return self.getProperties() # compatibility
600 
601  @classmethod
603  class collector:
604  pass
605 
606  # user provided defaults
607  c = collector()
608  cls.setDefaults( c )
609 
610  # defaults from C++
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
614 
615  return c.__dict__
616 
617  @classmethod
618  def getDefaultProperty( cls, name ):
619  class collector:
620  pass
621 
622  # user provided defaults
623  c = collector()
624  cls.setDefaults( c )
625 
626  if name in c.__dict__:
627  return c.__dict__[ name ]
628 
629  # defaults from C++
630  try:
631  v = cls._properties[name]
632  if hasattr( v, 'default' ):
633  return v.default
634  except KeyError:
635  pass
636 
637  return None
638 
639  def getProp(self, name):
640  """Returns the value of the given property.
641  """
642  if hasattr(self, name):
643  return getattr(self, name)
644  else:
645  return self.getDefaultProperties()[name]
646 
647  def setProp(self, name, value):
648  """Set the value of a given property
649  """
650  return setattr(self, name, value)
651 
652  def isPropertySet(self, name):
653  """Tell if the property 'name' has been set or not.
654 
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
657  set.
658  """
659  if not hasattr(self, name):
660  return False
661  else:
662  try:
663  default = self.getDefaultProperties()[name]
664  if isinstance(default, (list, dict)):
665  value = getattr(self, name)
666  return value != default
667  except KeyError:
668  pass # no default found
669  return True
670 
671  def getType( cls ):
672  return cls.__name__
673 
674  def getName( self ):
675  return self._name
676 
677  def name( self ):
678  return self.getName()
679 
680  def getJobOptName( self ): # full hierachical name
681  return self.getName()
682 
683  def isPublic( self ):
684  return True
685 
686  # for a couple of existing uses out there
687  def jobOptName( self ):
688  log.error( "jobOptName() is deprecated, use getJobOptName() instead for consistency%s",
689  error_explanation )
690  return self.getJobOptName() # compatibility
691 
692  def getFullName( self ) :
693  return str( self.getType() + '/' + self.getName() )
694 
695  def getFullJobOptName( self ):
696  return "%s/%s" % (self.getType(),self.getJobOptName() or self.getName())
697 
698  def getPrintTitle(self):
699  return self.getGaudiType() + ' ' + self.getTitleName()
700 
701  def getTitleName( self ):
702  if log.isEnabledFor( logging.DEBUG ):
703  return self.getFullJobOptName()
704  else:
705  return self.getFullName()
706 
707  def setDefaults( cls, handle ):
708  pass
709 
710  def clone( self, name = None, **kwargs ) :
711  if not name :
712  if hasattr(self, 'DefaultedName' ) : name = self.DefaultedName
713  else : name = self.getType()
714 
715  newconf = Configurable.__new__( self.__class__, name )
716  self.__class__.__init__( newconf, name )
717 
718  for proxy in self._properties.values():
719  try :
720  value = proxy.__get__( self )
721  if type(value) in [ str, list, dict, tuple ]:
722  # clone the values of the properties for basic types
723  value = type(value)(value)
724  proxy.__set__( newconf, value )
725  except AttributeError:
726  pass
727 
728  for c in self.__children:
729  newconf += c # processes proper copy semantics
730 
731  for n , t in self.__tools.items():
732  newconf.addTool(t, n)
733 
734  for name, value in kwargs.items():
735  setattr(newconf, name, value)
736 
737  return newconf
738 
739  def splitName( self ) :
740  fullname = self.getName()
741  dot = fullname.find('.')
742  if dot != -1 :
743  parentname = fullname[:dot]
744  longname = fullname[dot+1:]
745  else :
746  parentname = ''
747  longname = fullname
748  dot = longname.find('.')
749  if dot != -1 :
750  name = longname[:dot]
751  else :
752  name = longname
753  return parentname, name, longname
754 
755  def addTool( self, tool, name = None ) :
756  if isclass(tool) and issubclass(tool, ConfigurableAlgTool):
757  if name is None:
758  name = tool.__name__
759  priv_tool = tool( self.getName()+ '.' + name )
760  elif isinstance(tool, ConfigurableAlgTool):
761  if name is None:
762  name = tool.splitName()[1]
763  priv_tool = tool.clone( self.getName()+ '.' + name )
764  else:
765  if isclass(tool):
766  classname = tool.__name__
767  else:
768  classname = type(tool).__name__
769  raise TypeError, "addTool requires AlgTool configurable. Got %s type" % classname
770  self.__tools[name] = priv_tool
771  if name in self.__slots__:
772  # this is to avoid that the property hides the tool
773  setattr(self,name,self.__tools[name])
774 
775  def _isInSetDefaults( self ):
776  return self._inSetDefaults
777 
778  def __setupServices( self ):
779  #svcs = self.getServices()
780  #if not svcs:
781  svcs = []
782  #elif type(svcs) == types.StringType:
783  # svcs = [ svcs ]
784 
785  import __main__
786  for svc in svcs:
787  handle = __main__.Service( svc )
788  # services should be configurables as well, but aren't for now
789  # handle.setup()
790 
791  # allow Configurable to make some changes
792  if hasattr( self, 'configure' + svc ):
793  eval( 'self.configure' + svc + '( handle )' )
794 
795  def __setupDlls( self ):
796  dlls = self.getDlls()
797  if not dlls:
798  dlls = []
799  elif type(dlls) == types.StringType:
800  dlls = [ dlls ]
801 
802  from __main__ import theApp
803  dlls = filter( lambda d: d not in theApp.Dlls, dlls )
804  if dlls: theApp.Dlls += dlls
805 
806  def __setupDefaults( self ):
807  # set handle defaults flags to inform __setattr__ that it is being
808  # called during setDefaults of the concrete Configurable
809  self._inSetDefaults = True
810  self.setDefaults( self )
811  self._inSetDefaults = False
812 
813  @staticmethod
814  def _printHeader( indentStr, title ):
815  preLen = Configurable.printHeaderPre
816  postLen = Configurable.printHeaderWidth - preLen - 3 - len(title)# - len(indentStr)
817  postLen = max(preLen,postLen)
818  return indentStr + '/%s %s %s' % (preLen*'*',title,postLen*'*')
819 
820  @staticmethod
821  def _printFooter( indentStr, title ):
822  preLen = Configurable.printHeaderPre
823  postLen = Configurable.printHeaderWidth - preLen - 12 - len(title)# - len(indentStr)
824  postLen = max(preLen,postLen)
825  return indentStr + '\\%s (End of %s) %s' % (preLen*'-',title,postLen*'-')
826 
827  def __repr__( self ):
828  return '<%s at %s>' % (self.getFullJobOptName(),hex(id(self)))
829 
830  def __str__( self, indent = 0, headerLastIndentUnit=indentUnit ):
831  global log # to print some info depending on output level
832  indentStr = indent*Configurable.indentUnit
833  # print header
834  title = self.getPrintTitle()
835  # print line to easily see start-of-configurable
836  if indent > 0:
837  headerIndent = (indent-1)*Configurable.indentUnit + headerLastIndentUnit
838  else:
839  headerIndent = ''
840  rep = Configurable._printHeader( headerIndent, title )
841  rep += os.linesep
842  # print own properties
843  props = self.getProperties()
844  defs = self.getDefaultProperties()
845  if not props:
846  rep += indentStr + '|-<no properties>' + os.linesep
847  else:
848  # get property name with
849  nameWidth = 0
850  for p in props.keys():
851  nameWidth=max(nameWidth,len(p))
852  for p, v in props.items():
853  # start with indent and property name
854  prefix = indentStr + '|-%-*s' % (nameWidth,p)
855  # add memory address for debugging (not for defaults)
856  if log.isEnabledFor( logging.DEBUG ):
857  if v != Configurable.propertyNoValue:
858  address = ' @%11s' % hex(id(v))
859  else:
860  address = 13*' '
861  prefix += address
862  # add value and default
863  default = defs.get(p)
864  if v == Configurable.propertyNoValue:
865  # show default value as value, and no extra 'default'
866  strVal = repr(default)
867  strDef = None
868  else:
869  # convert configurable to handle
870  if hasattr(v,"getGaudiHandle"):
871  vv = v.getGaudiHandle()
872  else:
873  vv = v
874  if isinstance(vv,GaudiHandle) or isinstance(vv,GaudiHandleArray):
875  strVal = repr(vv)
876  if hasattr(default,"toStringProperty"): # the default may not be a GaudiHandle (?)
877  strDef = repr(default.toStringProperty())
878  else:
879  strDef = repr(default)
880  if strDef == repr(vv.toStringProperty()):
881  strDef = None
882  else:
883  strVal = repr(vv)
884  strDef = repr(default)
885  # add the value
886  line = prefix + ' = ' + strVal
887  # add default if present
888  if strDef is not None:
889  # put default on new line if too big
890  if len(line) + len(strDef) > Configurable.printHeaderWidth:
891  line += os.linesep + indentStr + '| ' + (len(prefix)-len(indentStr)-3)*' '
892  line += ' (default: %s)' % (strDef,)
893  # add the line to the total string
894  rep += line + os.linesep
895  # print out full private configurables
896 ## if isinstance(v,Configurable) and not v.isPublic():
897 ## rep += v.__str__( indent + 1 ) + os.linesep
898 ## elif isinstance(v,GaudiHandleArray):
899 ## for vi in v:
900 ## if isinstance(vi,Configurable) and not vi.isPublic():
901 ## rep += vi.__str__( indent + 1 ) + os.linesep
902 
903  # print configurables + their properties, or loop over sequence
904 ## for cfg in self.__children:
905  for cfg in self.getAllChildren():
906  rep += cfg.__str__( indent + 1, '|=' ) + os.linesep
907 
908  # print line to easily see end-of-configurable. Note: No linesep!
909  rep += Configurable._printFooter( indentStr, title )
910  return rep
911 
912 ### classes for generic Gaudi component ===========
913 class DummyDescriptor( object ):
914  def __init__( self, name ):
915  self.__name__ = name # conventional
916 
917  def __get__( self, obj, type = None ):
918  return getattr( obj, self.__name__ )
919 
920  def __set__( self, obj, value ):
921  object.__setattr__( obj, self.__name__, value )
922 
924  #__slots__ = { }
925 
926  def __init__( self, name = Configurable.DefaultName ):
927  Configurable.__init__( self, name )
928  self._name = name
929  self._properties = {}
930 
931  def __deepcopy__( self, memo ):
932  return self # algorithms are always shared
933 
934  def getGaudiType( self ): return 'GenericComponent'
935  def getDlls( self ) : pass
936  def getHandle( self ) : pass
937 
938  def __setattr__( self, name, value ):
939  # filter private (user) variables
940  if name[0] == '_':
941  super( ConfigurableGeneric, self ).__setattr__( name, value )
942  return
943 
944  # filter configurable types
945  if isinstance( value, Configurable ):
946  self.__dict__[ name ] = value
947  return
948 
949  # assume all the rest are properties
950  if not name in self._properties:
951  self._properties[ name ] = PropertyProxy( DummyDescriptor( name ) )
952  self._properties[ name ].__set__( self, value )
953 
954  def getJobOptName( self ): return None
955 
956 
957 ### base classes for individual Gaudi algorithms/services/algtools ===========
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 }
963 
964  def __init__( self, name = Configurable.DefaultName ):
965  super( ConfigurableAlgorithm, self ).__init__( name )
966  name = self.getName()
967  self._jobOptName = name[ name.find('/')+1 : ] # strips class
968 
969  def __deepcopy__( self, memo ):
970  return self # algorithms are always shared
971 
972  def getHandle( self ):
973  return iAlgorithm( self.getJobOptName() )
974 
975  def getGaudiType( self ):
976  return 'Algorithm'
977 
978  def getJobOptName( self ):
979  return self._jobOptName
980 
981 
983  __slots__ = { 'OutputLevel' : 0, \
984  'AuditServices' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
985 
986  def __deepcopy__( self, memo ):
987  return self # services are always shared
988 
989  def copyChild( self, child ):
990  return child # full sharing
991 
992  def getHandle( self ):
993  return iService( self._name )
994 
995  def getGaudiType( self ):
996  return 'Service'
997 
998  def getGaudiHandle( self ):
999  return ServiceHandle( self.toStringProperty() )
1000 
1001  def toStringProperty( self ):
1002  # called on conversion to a string property for the jocat
1003  return self.getName()
1004 
1005 
1007  __slots__ = { '_jobOptName' : '', 'OutputLevel' : 0, \
1008  'AuditTools' : 0, 'AuditInitialize' : 0, 'AuditFinalize' : 0 }
1009 
1010  def __init__( self, name = Configurable.DefaultName ):
1011  super( ConfigurableAlgTool, self ).__init__( name )
1012  if '.' not in self._name:
1013  # Public tools must have ToolSvc as parent
1014  self._name = "ToolSvc." + self._name
1015  name = self.getName()
1016  name = name[ name.find('/')+1 : ] # strips class, if any
1017  self._jobOptName = name
1018 
1019  def getHandle( self ):
1020  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1021  return iProperty( self.getJobOptName() )
1022 
1023  def getGaudiType( self ):
1024  return 'AlgTool'
1025 
1026  def getGaudiHandle( self ):
1027  if self.isPublic():
1028  return PublicToolHandle( self.toStringProperty() )
1029  else:
1030  return PrivateToolHandle( self.toStringProperty() )
1031 
1032  def getPrintTitle(self):
1033  if self.isPublic():
1034  pop = 'Public '
1035  else:
1036  pop = 'Private '
1037  return pop + Configurable.getPrintTitle(self)
1038 
1039  def setParent( self, parentName ):
1040 # print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
1041 # print "Calling stack:"
1042 # import traceback
1043 # traceback.print_stack()
1044  # propagate parent to AlgTools in children
1045  for c in self.getAllChildren():
1046  if isinstance(c,ConfigurableAlgTool): c.setParent( parentName )
1047 
1048  # update my own parent
1049  name = self.getName()
1050  name = name[name.rfind('.')+1:] # Name of the instance
1051  self._jobOptName = self._name = parentName + '.' + name
1052 
1053  def getParent( self ):
1054  dot = self._jobOptName.rfind('.')
1055  if dot != -1:
1056  return self._jobOptName[:dot]
1057  else:
1058  return ""
1059 
1060  def hasParent( self, parent ):
1061  return self._jobOptName.startswith( parent + '.' )
1062 
1063  def getJobOptName( self ):
1064  return self._jobOptName
1065 
1066  def isPublic( self ):
1067  return self.isInToolSvc()
1068 
1069  def isInToolSvc( self ):
1070  return self._jobOptName.startswith('ToolSvc.')
1071 
1072  def toStringProperty( self ):
1073  # called on conversion to a string property for the jocat
1074  return self.getFullName()
1075 
1076  def getFullName( self ) :
1077  # for Tools, the "full name" means "Type/LocalName",
1078  # without the names of the parents
1079  name = self.getName()
1080  # strip off everything before the last '.'
1081  name = name[name.rfind('.')+1:]
1082  return str( self.getType() + '/' + name )
1083 
1084 
1085 ### FIXME: this is just a placeholder, waiting for a real implementation
1086 ### It is sufficient to get us going... (and import a PkgConf which
1087 ### happens to contain an Auditor...)
1089  __slots__ = { '_jobOptName' : 0, 'OutputLevel' : 0, \
1090  'Enable' : 1 }
1091 
1092  def __init__( self, name = Configurable.DefaultName ):
1093  super( ConfigurableAuditor, self ).__init__( name )
1094  name = self.getName()
1095  name = name[ name.find('/')+1 : ] # strips class, if any
1096  self._jobOptName = name
1097 
1098  def getHandle( self ):
1099  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1100  return iProperty( self.getJobOptName() )
1101 
1102  def getGaudiType( self ):
1103  return 'Auditor'
1104 
1105  def getJobOptName( self ):
1106  return self._jobOptName
1107 
1108  def toStringProperty( self ):
1109  # called on conversion to a string property for the jocat
1110  return self.getType() + '/' + self.getName()
1111 
1113  __slots__ = { "__users__": [],
1114  "__used_instances__": [],
1115  "_enabled": True }
1116  ## list of ConfigurableUser classes this one is going to modify in the
1117  # __apply_configuration__ method.
1118  # The list may contain class objects, strings representing class objects or
1119  # tuples with the class object (or a string) as first element and the instance
1120  # name as second element.
1121  # If the instance name is None or not present, the function _instanceName()
1122  # is used to determine the name of the instance (the default implementation
1123  # returns "<this name>_<other name>".
1124  __used_configurables__ = []
1125  ## list of ConfigurableUser classes this one is going to query in the
1126  # __apply_configuration__ method
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():
1131  setattr(self, n, v)
1132  self._enabled = _enabled
1133  self.__users__ = []
1134 
1135  # Needed to retrieve the actual class if the declaration in __used_configurables__
1136  # and __queried_configurables__ is done with strings.
1137  from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
1138 
1139  # Set the list of users of the used configurables
1140  #
1142  for used in self.__used_configurables__:
1143  # By default we want to use the default name of the instances
1144  # for the used configurables
1145  used_name = Configurable.DefaultName
1146  # If the entry in the list is a tuple, we need a named instance
1147  if type(used) is tuple:
1148  used, used_name = used # we re-set used to re-use the code below
1149  if not used_name:
1150  used_name = self._instanceName(used)
1151  # Check is 'used' is a string or not
1152  if type(used) is str:
1153  used_class = confDbGetConfigurable(used)
1154  else:
1155  used_class = used
1156  # Instantiate the configurable that we are going to use
1157  try:
1158  inst = used_class(name = used_name, _enabled = False)
1159  except AttributeError:
1160  # This cover the case where the used configurable is not a
1161  # ConfigurableUser instance, i.e. id doesn't have the attribute
1162  # '_enabled'.
1163  inst = used_class(name = used_name)
1164  self.__addActiveUseOf(inst)
1165  for queried in self.__queried_configurables__:
1166  try:
1167  if type(queried) is str:
1168  queried = confDbGetConfigurable(used)
1169  inst = queried(_enabled = False)
1170  except AttributeError:
1171  inst = queried()
1172  self.__addPassiveUseOf(inst)
1173  def __addActiveUseOf(self, other):
1174  """
1175  Declare that we are going to modify the Configurable 'other' in our
1176  __apply_configuration__.
1177  """
1178  self.__used_instances__.append(other)
1179  if hasattr(other, "__users__"): # allow usage of plain Configurables
1180  other.__users__.append(self)
1181  def __addPassiveUseOf(self, other):
1182  """
1183  Declare that we are going to retrieve property values from the
1184  ConfigurableUser 'other' in our __apply_configuration__.
1185  """
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)
1189  def getGaudiType( self ):
1190  return 'User'
1191  def getDlls( self ):
1192  return None
1193  def getHandle( self ):
1194  return None
1195 
1196  def __detach_used__(self):
1197  """
1198  Remove this ConfigurableUser instance from the users list of the used
1199  instances.
1200  """
1201  for used in self.__used_instances__:
1202  if hasattr(used, "__users__"): # allow usage of plain Configurables
1203  used.__users__.remove(self)
1204 
1205  def propagateProperty(self, name, others = None, force = True):
1206  """
1207  Propagate the property 'name' (if set) to other configurables (if possible).
1208  'others' can be:
1209  None:
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.
1215 
1216 
1217  The logic is:
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
1222  """
1223  # transform 'others' to a list of configurable instances
1224  if others is None:
1225  others = self.__used_instances__
1226  elif type(others) not in [ list, tuple ] :
1227  others = [ others ]
1228  # these can be computed before the loop
1229  local_is_set = self.isPropertySet(name)
1230  value = self.getProp(name)
1231  # loop over the others that do have 'name' in their slots
1232  for other in [ o for o in others if name in o.__slots__ ]:
1233  # If self property is set, use it
1234  if local_is_set:
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(),
1239  "prop": name } )
1240  other.setProp(name, value)
1241  # If not, and other property also not set, propagate the default
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]:
1247  # Special case for list and dictionaries:
1248  # also set the property to the same value of the default (copy)
1249  other.setProp(name, otherType(value))
1250  else:
1251  other.setProp(name, value)
1252  # If not set and other set, do nothing
1253 
1254  def propagateProperties(self, names = None, others = None, force = True):
1255  """
1256  Call propagateProperty for each property listed in 'names'.
1257  If 'names' is None, all the properties are propagated.
1258  """
1259  if names is None:
1260  # use all the non-private slots
1261  names = [ p for p in self.__slots__ if not p.startswith("_") ]
1262  for n in names:
1263  self.propagateProperty(n, others, force)
1264 
1266  """
1267  Function to be overridden to convert the high level configuration into a
1268  low level one.
1269  The default implementation calls applyConf, which is the method defined
1270  in some ConfigurableUser implementations.
1271  """
1272  return self.applyConf()
1273 
1274  def applyConf( self ):
1275  """
1276  Function to be overridden to convert the high level configuration into a
1277  low level one.
1278  """
1279  pass
1280 
1281  def _instanceName(self, cls):
1282  """
1283  Function used to define the name of the private instance of a given class
1284  name.
1285  This method is used when the __used_configurables_property__ declares the
1286  need of a private used configurable without specifying the name.
1287  """
1288  if type(cls) is str:
1289  clName = cls
1290  else:
1291  clName = cls.__name__
1292  return "%s_%s" % (self.name(), clName)
1293 
1294  def getUsedInstance(self, name):
1295  """
1296  Return the used instance with a given name.
1297  """
1298  for i in self.__used_instances__:
1299  if i.name() == name:
1300  if hasattr(i, "_enabled"):
1301  # ensure that the instances retrieved through the method are
1302  # enabled
1303  i._enabled = True
1304  return i
1305  raise KeyError(name)
1306 
1307 # list of callables to be called after all the __apply_configuration__ are called.
1308 postConfigActions = []
1310  """
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'.
1314  """
1315  try:
1316  postConfigActions.remove(function)
1317  except:
1318  pass
1319  postConfigActions.append(function)
1321  """
1322  Remove a collable from the list of post-config actions.
1323  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1324  """
1325  postConfigActions.remove(function)
1326 
1327 _appliedConfigurableUsers_ = False
1329  """
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.
1333  """
1334  # Avoid double calls
1335  global _appliedConfigurableUsers_, postConfigActions
1336  if _appliedConfigurableUsers_:
1337  return
1338  _appliedConfigurableUsers_ = True
1339 
1340  confUsers = [ c
1341  for c in Configurable.allConfigurables.values()
1342  if hasattr(c,"__apply_configuration__") ]
1343  applied = True # needed to detect dependency loops
1344  while applied and confUsers:
1345  newConfUsers = [] # list of conf users that cannot be applied yet
1346  applied = False
1347  for c in confUsers:
1348  if hasattr(c,"__users__") and c.__users__:
1349  newConfUsers.append(c) # cannot use this one yet
1350  else: # it does not have users or the list is empty
1351  applied = True
1352  # the ConfigurableUser is enabled if it doesn't have an _enabled
1353  # property or its value is True
1354  enabled = (not hasattr(c, "_enabled")) or c._enabled
1355  if enabled:
1356  log.info("applying configuration of %s", c.name())
1357  c.__apply_configuration__()
1358  log.info(c)
1359  else:
1360  log.info("skipping configuration of %s", c.name())
1361  if hasattr(c, "__detach_used__"):
1362  # tells the used configurables that they are not needed anymore
1363  c.__detach_used__()
1364  confUsers = newConfUsers # list of C.U.s still to go
1365  if confUsers:
1366  # this means that some C.U.s could not be applied because of a dependency loop
1367  raise Error("Detected loop in the ConfigurableUser "
1368  " dependencies: %r" % [ c.name()
1369  for c in confUsers ])
1370  # Call post-config actions
1371  for action in postConfigActions:
1372  action()
1373 
1375  """
1376  Function to select all and only the configurables that have to be used in
1377  GaudiPython.AppMgr constructor.
1378  This is needed because in Athena the implementation have to be different (the
1379  configuration is used in a different moment).
1380  """
1381  return [ k
1382  for k, v in Configurable.allConfigurables.items()
1383  if v.getGaudiType() != "User" ] # Exclude ConfigurableUser instances
1384 
1385 def purge():
1386  """
1387  Clean up all configurations and configurables.
1388  """
1389  for c in Configurable.allConfigurables.values():
1390  c.__class__.configurables.clear()
1391  Configurable.allConfigurables.clear()
1392  # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
1393  # migrated to the correct class when this is known.
1394  ConfigurableGeneric.configurables.clear()
1395  from ProcessJobOptions import _included_files
1396  import os.path, sys
1397  for file in _included_files:
1398  dirname, basname = os.path.split(file)
1399  basname, ext = os.path.splitext(basname)
1400  if basname in sys.modules:
1401  del sys.modules[basname]
1402  _included_files.clear()

Generated at Wed Nov 28 2012 12:17:15 for Gaudi Framework, version v23r5 by Doxygen version 1.8.2 written by Dimitri van Heesch, © 1997-2004