The Gaudi Framework  v29r0 (ff2e7097)
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
6 import string
7 import types
8 import os
9 import sys
10 from inspect import isclass
11 import GaudiKernel.ConfigurableMeta as ConfigurableMeta
12 from GaudiKernel.Constants import error_explanation, \
13  VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL
14 from GaudiKernel.PropertyProxy import PropertyProxy
15 from GaudiKernel.GaudiHandles import *
17 
18 from GaudiConfig.ControlFlow import (OrNode, AndNode, OrderedNode,
19  ignore, InvertNode,
20  ControlFlowLeaf, ControlFlowNode,
21  CFTrue, CFFalse)
22 
23 # data ---------------------------------------------------------------------
24 __all__ = ['Configurable',
25  'ConfigurableAlgorithm',
26  'ConfigurableAlgTool',
27  'ConfigurableAuditor',
28  'ConfigurableService',
29  'ConfigurableUser',
30  'VERBOSE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'FATAL',
31  'appendPostConfigAction', 'removePostConfigAction']
32 
33 # for messaging
34 import logging
35 log = logging.getLogger('Configurable')
36 
37 
38 def expandvars(data):
39  """
40  Expand environment variables "data".
41  Data can be string, list, tuple and dictionary. For collection, all the
42  contained strings will be manipulated (recursively).
43  """
44  import os.path
45  typ = type(data)
46  if typ is str:
47  return os.path.expandvars(data)
48  elif typ in [list, tuple]:
49  collect = []
50  for i in data:
51  collect.append(expandvars(i))
52  return typ(collect)
53  elif typ is dict:
54  collect = {}
55  for k in data:
56  collect[expandvars(k)] = expandvars(data[k])
57  return collect
58  return data
59 
60 
61 class Error(RuntimeError):
62  """
63  Error occurred in the configuration process.
64  """
65  pass
66 
67 # Allow references to options as in old style
68 
69 
70 class PropertyReference(object):
71  def __init__(self, propname):
72  self.name = propname
73 
74  def __str__(self):
75  return "@%s" % self.name
76 
77  def __resolve__(self):
78  # late binding for property references
79  retval = None
80  refname, refprop = self.name.rsplit('.', 1)
81  if refname in Configurable.allConfigurables:
82  conf = Configurable.allConfigurables[refname]
83  retval = getattr(conf, refprop)
84  if hasattr(retval, "getFullName"):
85  retval = retval.getFullName()
86  else:
87  raise NameError("name '%s' not found resolving '%s'" %
88  (refname, self))
89  return retval
90 
91  def getFullName(self):
92  """This function allow transparent integration with
93  Configurable.getValuedProperties.
94  """
95  try:
96  return self.__resolve__()
97  except NameError:
98  # ignore the error if we cannot resolve the name yet
99  return self
100  except AttributeError:
101  # ignore the error if we cannot resolve the attribute yet
102  return self
103 
104 # base class for configurable Gaudi algorithms/services/algtools/etc. ======
105 
106 
107 class Configurable(object):
108  """Base class for Gaudi components that implement the IProperty interface.
109  Provides most of the boilerplate code, but the actual useful classes
110  are its derived ConfigurableAlgorithm, ConfigurableService, and
111  ConfigurableAlgTool."""
112 
113  # for detecting the default name
114  class DefaultName:
115  pass
116 
117  propertyNoValue = '<no value>'
118  indentUnit = '| '
119  printHeaderWidth = 100
120  printHeaderPre = 5
121 
123 
124  __slots__ = (
125  '__children', # controlled components, e.g. private AlgTools
126  '__tools', # private AlgTools (#PM-->)
127  '_name', # the (unqualified) component name
128  '_inSetDefaults', # currently setting default values
129  '_initok', # used to enforce base class init
130  '_setupok' # for debugging purposes (temporary)
131  )
132 
133  allConfigurables = {} # just names would do, but currently refs to the actual
134  configurableServices = {} # just names would do, but currently refs to the actual
135  # configurables is needed for (temporary) backwards
136  # compatibility; will change in the future
137  _configurationLocked = False
138 
139  def __new__(cls, *args, **kwargs):
140  """To Gaudi, any object with the same type/name is the same object. Hence,
141  this is mimicked in the configuration: instantiating a new Configurable
142  of a type with the same name will return the same instance."""
143 
144  global log
145  # try to get the name of the Configurable (having a name is compulsory)
146  if 'name' in kwargs:
147  # simple keyword (by far the easiest)
148  name = kwargs['name']
149  elif 'name' in cls.__init__.func_code.co_varnames:
150  # either positional in args, or default
151  index = list(cls.__init__.func_code.co_varnames).index('name')
152  try:
153  # var names index is offset by one as __init__ is to be called with self
154  name = args[index - 1]
155  except IndexError:
156  # retrieve default value, then
157  name = cls.__init__.func_defaults[index - (len(args) + 1)]
158  else:
159  # positional index is assumed (will work most of the time)
160  try:
161  name = args[1] # '0' is for self
162  except (IndexError, TypeError):
163  raise TypeError(
164  'no "name" argument while instantiating "%s"' % cls.__name__)
165 
166  argname = name
167  if name == Configurable.DefaultName:
168  if hasattr(cls, 'DefaultedName'):
169  name = cls.DefaultedName
170  else:
171  name = cls.getType()
172  elif not name or type(name) != str:
173  # unnamed, highly specialized user code, etc. ... unacceptable
174  raise TypeError(
175  'could not retrieve name from %s.__init__ arguments' % cls.__name__)
176 
177  # Handle the case of global tools to prepend ToolSvc in the name.
178  # This is needed for compatibility with old JobOptions files being read
179  if issubclass(cls, ConfigurableAlgTool) and '.' not in name:
180  name = 'ToolSvc.' + name
181 
182  # close backdoor access to otherwise private subalgs/tools
183  # PM if 0 <= name.find( '.' ):
184  # PM # temp protection for old style types
185  # PM from OldStyleConfig import GenericConfigurable
186  # PM if not issubclass( cls, GenericConfigurable ): # except raised for new types only
187  # PM raise NameError( '"%s": backdoor access to private configurables not allowed' % name )
188 
189  # ordinary recycle case
190  if name in cls.configurables:
191  conf = cls.configurables[name]
192  if name != argname: # special case: user derived <-> real ... make same
193  cls.configurables[conf.getType()] = conf
194  #---PM: Initialize additional properties
195  for n, v in kwargs.items():
196  if n != "name": # it should not be confused with a normal property
197  setattr(conf, n, v)
198  if not cls._configurationLocked and not "_enabled" in kwargs and isinstance(conf, ConfigurableUser):
199  # Ensure that the ConfigurableUser gets enabled if nothing is
200  # specified in the constructor.
201  setattr(conf, "_enabled", True)
202  return conf
203 
204  # a couple of special cases (note that these cases don't mix)
205  spos = name.find('/')
206  ti_name = None
207  if spos < 0:
208  ti_name = "%s/%s" % (name, name)
209  if ti_name in cls.configurables:
210  # support for old-style name as type/name lookup where name==type
211  return cls.configurables[ti_name]
212 
213  i_name = None
214  if spos > 0:
215  i_name = name[:spos]
216  if i_name == name[spos + 1:] and i_name in cls.configurables:
217  # this is the opposite of the above special case
218  return cls.configurables[i_name]
219 
220  # the following is purely for debugging support and should realistically bomb
221  conf = cls.allConfigurables.get(name, None) or\
222  (spos < 0 and cls.allConfigurables.get(ti_name, None)) or\
223  (spos > 0 and i_name == name[spos + 1:]
224  and cls.allConfigurables.get(i_name, None))
225  if conf: # wrong type used?
226  if conf.__class__ is ConfigurableGeneric:
227  # If the instance found is ConfigurableGeneric then
228  # we create a new one with the proper type and fill with
229  # the contents of the generic one
230  newconf = object.__new__(cls)
231  cls.__init__(newconf, *args, **kwargs)
232  # initialize with the properties of generic configurable
233  # (we map the names of the properties to lowercase versions because
234  # old options are not case sensitive)
235  names = {}
236  for n in newconf.__slots__:
237  names[n.lower()] = n
238  for n in conf._properties:
239  if names[n.lower()] != n:
240  log.warning("Option '%s' was used for %s, but the correct spelling is '%s'" % (
241  n, name, names[n.lower()]))
242  setattr(newconf, names[n.lower()], getattr(conf, n))
243  for n, v in kwargs.items():
244  setattr(newconf, n, v)
245  cls.configurables[name] = newconf
246  cls.allConfigurables[name] = newconf
247  return newconf
248  else:
249  # will be an actual error in the future (now only report as such)
250  log.error('attempt to redefine type of "%s" (was: %s, new: %s)%s',
251  name, conf.__class__.__name__, cls.__name__, error_explanation)
252  # in the future:
253  # return None # will bomb on use (or go unharmed on non-use)
254  # for now, allow use through allConfigurables lookup
255  #---PM: Initialize additional properties
256  for n, v in kwargs.items():
257  setattr(conf, n, v)
258  return conf
259 
260  # still here: create a new instance and initialize it
261  conf = object.__new__(cls)
262  cls.__init__(conf, *args, **kwargs)
263 
264  # update normal, per-class cache
265  cls.configurables[name] = conf
266 
267  for base in cls.__bases__:
268  if base.__name__ == 'ConfigurableService':
269  cls.configurableServices[name] = conf
270 
271  # update generics super-cache, if needed
272  cls.allConfigurables[name] = conf
273  #-->PM#if hasattr( cls, 'getType' ) and name.find('/') < 0:
274  #-->PM# cls.allConfigurables[ cls.getType() + '/' + name ] = conf
275 
276  return conf
277 
278  def __init__(self, name=DefaultName):
279  # check class readiness, all required overloads should be there now
280  klass = self.__class__
281 
282  # this is an abstract class
283  if klass == Configurable:
284  raise TypeError, "%s is an ABC and can not be instantiated" % str(
285  Configurable)
286 
287  # the following methods require overloading
288  # NOT YET meths = { 'getServices' : 1, # retrieve list of services to configure
289  meths = {'getDlls': 1, # provide list of Dlls to load
290  'getGaudiType': 1, # return string describing component class
291  'getHandle': 1} # provide access to C++ side component instance
292 # 'getType' : 1 } # return the type of the actual C++ component
293 
294  for meth, nArgs in meths.items():
295  try:
296  f = getattr(klass, meth).im_func
297  except AttributeError:
298  raise NotImplementedError, "%s is missing in class %s" % (
299  meth, str(klass))
300 
301  # in addition, verify the number of arguments w/o defaults
302  nargcount = f.func_code.co_argcount
303  ndefaults = f.func_defaults and len(f.func_defaults) or 0
304  if not nargcount - ndefaults <= nArgs <= nargcount:
305  raise TypeError, "%s.%s requires exactly %d arguments" % (
306  klass, meth, nArgs)
307 
308  # for using this Configurable as a (Gaudi) sequence
309  self.__children = []
310  self.__tools = {}
311 
312  # know who we are
313  if name == Configurable.DefaultName:
314  if hasattr(self.__class__, 'DefaultedName'):
315  self._name = self.__class__.DefaultedName
316  else:
317  self._name = self.getType()
318  else:
319  self._name = name
320 
321  # set to True when collecting defaults, False otherwise
322  self._inSetDefaults = False
323 
324  # for later, in case __init__ itself is overridden
325  self._initok = True
326 
327  # for debugging purposes (temporary)
328  self._setupok = False
329 
330  # pickle support
331  def __getstate__(self):
332  dict = {}
333  for name, proxy in self._properties.items():
334  try:
335  dict[name] = proxy.__get__(self)
336  except AttributeError:
337  pass
338 
339  dict['_Configurable__children'] = self.__children
340  dict['_Configurable__tools'] = self.__tools
341  dict['_name'] = self._name
342  return dict
343 
344  def __getnewargs__(self):
345  return (self._name,)
346 
347  def __setstate__(self, dict):
348  self._initok = True
349  for n, v in dict.items():
350  setattr(self, n, v)
351  return
352 
353  # to allow a few basic sanity checks, as well as nice syntax
354  def __len__(self):
355  return len(self.__children)
356 
357  def __iter__(self):
358  return iter(self.__children)
359 
360  # ownership rules of self through copying
361  def __deepcopy__(self, memo):
362  newconf = object.__new__(self.__class__)
363  self.__class__.__init__(newconf, self.getName())
364 
365  for proxy in self._properties.values():
366  try:
367  proxy.__set__(newconf, proxy.__get__(self))
368  except AttributeError:
369  pass # means property was not set for self
370 
371  for c in self.__children:
372  newconf += c # processes proper copy semantics
373 
374  return newconf
375 
376  # hierarchy building, and ownership rules of children
377  def __iadd__(self, configs, descr=None):
378  if not type(configs) in (list, tuple):
379  configs = (configs, )
380 
381  joname = self.getJobOptName()
382 
383  for cfg in configs:
384  # prevent type mismatches
385  if not isinstance(cfg, Configurable):
386  raise TypeError("'%s' is not a Configurable" % str(cfg))
387 
388  cc = self.copyChildAndSetParent(cfg, joname)
389 
390  # filters dupes; usually "ok" (backdoor should catch them)
391  ccjo = cc.getJobOptName()
392  for c in self.__children:
393  if c.getJobOptName() == ccjo:
394  log.error(
395  'attempt to add a duplicate ... dupe ignored%s', error_explanation)
396  break
397  else:
398  self.__children.append(cc)
399 
400  try:
401  if descr: # support for tool properties
402  descr.__set__(self, cc)
403  else:
404  setattr(self, cc.getName(), cc)
405  except AttributeError:
406  pass # to allow free addition of tools/subalgorithms
407 
408  return self
409 
410  def __getattr__(self, attr): # until ToolProperties exist ...
411 
412  if attr in self.__tools:
413  return self.__tools[attr]
414 
415  if attr in self._properties:
416  if isinstance(self._properties[attr].__get__(self), DataObjectHandleBase):
417  return self._properties[attr].__get__(self)
418 
419  for c in self.__children:
420  if c.getName() == attr:
421  return c
422 
423  raise AttributeError(
424  "'%s' object has no attribute '%s'" % (self.__class__, attr))
425 
426  def __setattr__(self, name, value):
427  if self._configurationLocked:
428  raise RuntimeError(
429  "%s: Configuration cannot be modified after the ApplicationMgr has been started." % self.name())
430  try:
431  super(Configurable, self).__setattr__(name, value)
432  except AttributeError:
433  raise AttributeError("Configurable '%s' does not have property '%s'."
434  % (self.__class__.__name__, name))
435 
436  def __delattr__(self, attr):
437  # remove as property, otherwise try as child
438  try:
439  # remove history etc., then reset to default (in case set before)
440  prop = self._properties[attr]
441  prop.__delete__(self)
442  prop.__set__(self, prop.default)
443  return # reaches here? was property: done now
444  except KeyError:
445  pass
446  # otherwise, remove the private tool
447  if attr in self.__tools:
448  del self.__tools[attr]
449 
450  # otherwise, remove child, if one is so named
451  for c in self.__children:
452  if c.getName() == attr:
453  self.__children.remove(c)
454 
455  # potentially, there are left over caches (certain user derived classes)
456  try:
457  del self.__dict__[attr]
458  except (AttributeError, KeyError):
459  pass
460 
461  def __nonzero__(self):
462  return True
463 
464  def remove(self, items):
465  if type(items) != list and type(items) != tuple:
466  items = [items]
467 
468  self.__children = [e for e in self.__children if not e in items]
469 
470  def removeAll(self):
471  self.remove(self.__children)
472 
473  # called by __iadd__; determines child copy semantics
474  def copyChild(self, child):
475  return copy.deepcopy(child)
476 
477  def setParent(self, parentName):
478  pass
479 
480  def getParent(self):
481  return ""
482 
483  def hasParent(self, parent):
484  return False
485 
486  def copyChildAndSetParent(self, cfg, parent):
487  cc = self.copyChild(cfg)
488 
489  if hasattr(cc, 'setParent') and parent:
490  try:
491  cc.setParent(parent)
492  except RuntimeError, e:
493  # temporary backdoor resolution for compatibility
494  log.error(str(e) + '%s', error_explanation)
495  ccbd = cc.configurables[cc.getJobOptName()]
496 
497  # merge properties, new over pre-existing
498  for proxy in self._properties.values():
499  if proxy.history.has_key(cc):
500  proxy.__set__(ccbd, proxy.__get__(cc))
501 
502  # consolidate
503  cc = ccbd
504  return cc
505 
506  def getChildren(self):
507  return self.__children[:] # read only
508 
509  def getTools(self):
510  return self.__tools.values() # read only
511 
512  def children(self):
513  log.error(
514  "children() is deprecated, use getChildren() instead for consistency")
515  log.error("getChildren() returns a copy; to add a child, use 'parent += child'%s",
516  error_explanation)
517  return self.__children # by ref, for compatibility
518 
519  def getAllChildren(self):
520  """Get all (private) configurable children, both explicit ones (added with +=)
521  and the ones in the private GaudiHandle properties"""
522  childs = []
523  # add private configurable properties (also inside handles)
524  for proxy in self._properties.values():
525  try:
526  c = proxy.__get__(self)
527  except AttributeError:
528  pass
529  else:
530  if isinstance(c, Configurable) and not c.isPublic():
531  childs.append(c)
532  elif isinstance(c, GaudiHandle):
533  try:
534  conf = c.configurable
535  except AttributeError:
536  pass
537  else:
538  if not conf.isPublic():
539  childs.append(conf)
540  elif isinstance(c, GaudiHandleArray):
541  # only setup private arrays
542  if not c.isPublic():
543  for ci in c:
544  if isinstance(ci, Configurable):
545  childs.append(ci)
546  else:
547  try:
548  conf = ci.configurable
549  except AttributeError:
550  pass
551  else:
552  childs.append(conf)
553 
554  # add explicit children
555  childs += self.__children
556  return childs
557 
558  def getSequence(self):
559  elems = []
560  for c in self.__children:
561  elems.append(c.getFullName())
562  return elems
563 
564  def setup(self):
565  # make sure base class init has been called
566  if not hasattr(self, '_initok') or not self._initok:
567  # could check more, but this is the only explanation
568  raise TypeError, \
569  "Configurable.__init__ not called in %s override" % self.__class__.__name__
570 
571 # log.debug("calling setup() on " + self.getFullJobOptName())
572 
573  # setup self: this collects all values on the python side
574  self.__setupServices()
575  self.__setupDlls()
576  self.__setupDefaults()
577 
578  # setup children
579  for c in self.getAllChildren():
580  c.setup()
581 
582  # now get handle to work with for moving properties into the catalogue
583  handle = self.getHandle()
584  if not handle:
585  log.debug('no handle for %s: not transporting properties', self._name)
586  return # allowed, done early
587 
588  # pass final set of properties on to handle on the C++ side or JobOptSvc
589  for name in self._properties.keys():
590  if hasattr(self, name): # means property has python-side value/default
591  setattr(handle, name, getattr(self, name))
592 
593  # for debugging purposes
594  self._setupok = True
595 
596  def getProperties(self):
597  props = {}
598  for name, proxy in self._properties.items():
599  try:
600  props[name] = proxy.__get__(self)
601  except AttributeError:
602  props[name] = Configurable.propertyNoValue
603 
604  return props
605 
607  """Get all properties with their description string as { name : (value, desc) }."""
608  props = {}
609  for name, proxy in self._properties.items():
610  try:
611  props[name] = (proxy.__get__(self), proxy.__doc__)
612  except AttributeError:
613  props[name] = (Configurable.propertyNoValue, proxy.__doc__)
614  return props
615 
617  props = {}
618  for name, proxy in self._properties.items():
619  if self.isPropertySet(name):
620  value = proxy.__get__(self)
621  if hasattr(value, 'getFullName'):
622  value = value.getFullName()
623  elif type(value) in [list, tuple]:
624  new_value = []
625  for i in value:
626  if hasattr(i, 'getFullName'):
627  new_value.append(i.getFullName())
628  else:
629  new_value.append(i)
630  value = type(value)(new_value)
631  elif type(value) is dict:
632  new_value = {}
633  for i in value:
634  if hasattr(value[i], 'getFullName'):
635  new_value[i] = value[i].getFullName()
636  else:
637  new_value[i] = value[i]
638  value = new_value
639  props[name] = value
640 
641  return props
642 
643  def properties(self):
644  return self.getProperties() # compatibility
645 
646  @classmethod
648  class collector:
649  pass
650 
651  # user provided defaults
652  c = collector()
653  cls.setDefaults(c)
654 
655  # defaults from C++
656  for k, v in cls._properties.items():
657  if not k in c.__dict__ and hasattr(v, 'default'):
658  c.__dict__[k] = v.default
659 
660  return c.__dict__
661 
662  @classmethod
663  def getDefaultProperty(cls, name):
664  class collector:
665  pass
666 
667  # user provided defaults
668  c = collector()
669  cls.setDefaults(c)
670 
671  if name in c.__dict__:
672  return c.__dict__[name]
673 
674  # defaults from C++
675  try:
676  v = cls._properties[name]
677  if hasattr(v, 'default'):
678  return v.default
679  except KeyError:
680  pass
681 
682  return None
683 
684  def getProp(self, name):
685  """Returns the value of the given property.
686  """
687  if hasattr(self, name):
688  return getattr(self, name)
689  else:
690  return self.getDefaultProperties()[name]
691 
692  def setProp(self, name, value):
693  """Set the value of a given property
694  """
695  return setattr(self, name, value)
696 
697  def isPropertySet(self, name):
698  """Tell if the property 'name' has been set or not.
699 
700  Because of a problem with list and dictionary properties, in those cases
701  if the value is equal to the default, the property is considered as not
702  set.
703  """
704  if not hasattr(self, name):
705  return False
706  default = self.getDefaultProperty(name)
707  if isinstance(default, (list, dict, DataObjectHandleBase)):
708  value = getattr(self, name)
709  return value != default
710  return True
711 
712  def getType(cls):
713  return cls.__name__
714 
715  def getName(self):
716  return self._name
717 
718  def name(self):
719  return self.getName()
720 
721  def getJobOptName(self): # full hierachical name
722  return self.getName()
723 
724  def isPublic(self):
725  return True
726 
727  # for a couple of existing uses out there
728  def jobOptName(self):
729  log.error("jobOptName() is deprecated, use getJobOptName() instead for consistency%s",
730  error_explanation)
731  return self.getJobOptName() # compatibility
732 
733  def getFullName(self):
734  return str(self.getType() + '/' + self.getName())
735 
736  def getFullJobOptName(self):
737  return "%s/%s" % (self.getType(), self.getJobOptName() or self.getName())
738 
739  def getPrintTitle(self):
740  return self.getGaudiType() + ' ' + self.getTitleName()
741 
742  def getTitleName(self):
743  if log.isEnabledFor(logging.DEBUG):
744  return self.getFullJobOptName()
745  else:
746  return self.getFullName()
747 
748  def setDefaults(cls, handle):
749  pass
750 
751  def clone(self, name=None, **kwargs):
752  if not name:
753  if hasattr(self, 'DefaultedName'):
754  name = self.DefaultedName
755  else:
756  name = self.getType()
757 
758  newconf = Configurable.__new__(self.__class__, name)
759  self.__class__.__init__(newconf, name)
760 
761  for proxy in self._properties.values():
762  try:
763  value = proxy.__get__(self)
764  if type(value) in [str, list, dict, tuple]:
765  # clone the values of the properties for basic types
766  value = type(value)(value)
767  proxy.__set__(newconf, value)
768  except AttributeError:
769  pass
770 
771  for c in self.__children:
772  newconf += c # processes proper copy semantics
773 
774  for n, t in self.__tools.items():
775  newconf.addTool(t, n)
776 
777  for name, value in kwargs.items():
778  setattr(newconf, name, value)
779 
780  return newconf
781 
782  def splitName(self):
783  fullname = self.getName()
784  dot = fullname.find('.')
785  if dot != -1:
786  parentname = fullname[:dot]
787  longname = fullname[dot + 1:]
788  else:
789  parentname = ''
790  longname = fullname
791  dot = longname.find('.')
792  if dot != -1:
793  name = longname[:dot]
794  else:
795  name = longname
796  return parentname, name, longname
797 
798  def addTool(self, tool, name=None):
799  if isclass(tool) and issubclass(tool, ConfigurableAlgTool):
800  if name is None:
801  name = tool.__name__
802  priv_tool = tool(self.getName() + '.' + name)
803  elif isinstance(tool, ConfigurableAlgTool):
804  if name is None:
805  name = tool.splitName()[1]
806  priv_tool = tool.clone(self.getName() + '.' + name)
807  else:
808  if isclass(tool):
809  classname = tool.__name__
810  else:
811  classname = type(tool).__name__
812  raise TypeError, "addTool requires AlgTool configurable. Got %s type" % classname
813  self.__tools[name] = priv_tool
814  if name in self.__slots__:
815  # this is to avoid that the property hides the tool
816  setattr(self, name, self.__tools[name])
817  return self.__tools[name]
818 
819  def _isInSetDefaults(self):
820  return self._inSetDefaults
821 
822  def __setupServices(self):
823  #svcs = self.getServices()
824  # if not svcs:
825  svcs = []
826  # elif type(svcs) == types.StringType:
827  # svcs = [ svcs ]
828 
829  import __main__
830  for svc in svcs:
831  handle = __main__.Service(svc)
832  # services should be configurables as well, but aren't for now
833  # handle.setup()
834 
835  # allow Configurable to make some changes
836  if hasattr(self, 'configure' + svc):
837  eval('self.configure' + svc + '( handle )')
838 
839  def __setupDlls(self):
840  dlls = self.getDlls()
841  if not dlls:
842  dlls = []
843  elif type(dlls) == types.StringType:
844  dlls = [dlls]
845 
846  from __main__ import theApp
847  dlls = filter(lambda d: d not in theApp.Dlls, dlls)
848  if dlls:
849  theApp.Dlls += dlls
850 
851  def __setupDefaults(self):
852  # set handle defaults flags to inform __setattr__ that it is being
853  # called during setDefaults of the concrete Configurable
854  self._inSetDefaults = True
855  self.setDefaults(self)
856  self._inSetDefaults = False
857 
858  @staticmethod
859  def _printHeader(indentStr, title):
860  preLen = Configurable.printHeaderPre
861  postLen = Configurable.printHeaderWidth - \
862  preLen - 3 - len(title) # - len(indentStr)
863  postLen = max(preLen, postLen)
864  return indentStr + '/%s %s %s' % (preLen * '*', title, postLen * '*')
865 
866  @staticmethod
867  def _printFooter(indentStr, title):
868  preLen = Configurable.printHeaderPre
869  postLen = Configurable.printHeaderWidth - \
870  preLen - 12 - len(title) # - len(indentStr)
871  postLen = max(preLen, postLen)
872  return indentStr + '\\%s (End of %s) %s' % (preLen * '-', title, postLen * '-')
873 
874  def __repr__(self):
875  return '{0}({1!r})'.format(self.__class__.__name__, self.name())
876 
877  def __str__(self, indent=0, headerLastIndentUnit=indentUnit):
878  global log # to print some info depending on output level
879  indentStr = indent * Configurable.indentUnit
880  # print header
881  title = self.getPrintTitle()
882  # print line to easily see start-of-configurable
883  if indent > 0:
884  headerIndent = (indent - 1) * \
885  Configurable.indentUnit + headerLastIndentUnit
886  else:
887  headerIndent = ''
888  rep = Configurable._printHeader(headerIndent, title)
889  rep += os.linesep
890  # print own properties
891  props = self.getProperties()
892  defs = self.getDefaultProperties()
893  if not props:
894  rep += indentStr + '|-<no properties>' + os.linesep
895  else:
896  # get property name with
897  nameWidth = 0
898  for p in props.keys():
899  nameWidth = max(nameWidth, len(p))
900  for p, v in props.items():
901  # start with indent and property name
902  prefix = indentStr + '|-%-*s' % (nameWidth, p)
903  # add memory address for debugging (not for defaults)
904  if log.isEnabledFor(logging.DEBUG):
905  if v != Configurable.propertyNoValue:
906  address = ' @%11s' % hex(id(v))
907  else:
908  address = 13 * ' '
909  prefix += address
910  # add value and default
911  default = defs.get(p)
912  if v == Configurable.propertyNoValue:
913  # show default value as value, and no extra 'default'
914  strVal = repr(default)
915  strDef = None
916  else:
917  # convert configurable to handle
918  if hasattr(v, "getGaudiHandle"):
919  vv = v.getGaudiHandle()
920  else:
921  vv = v
922  if isinstance(vv, GaudiHandle) or isinstance(vv, GaudiHandleArray):
923  strVal = repr(vv)
924  # the default may not be a GaudiHandle (?)
925  if hasattr(default, "toStringProperty"):
926  strDef = repr(default.toStringProperty())
927  else:
928  strDef = repr(default)
929  if strDef == repr(vv.toStringProperty()):
930  strDef = None
931  else:
932  strVal = repr(vv)
933  strDef = repr(default)
934  # add the value
935  line = prefix + ' = ' + strVal
936  # add default if present
937  if strDef is not None:
938  # put default on new line if too big
939  if len(line) + len(strDef) > Configurable.printHeaderWidth:
940  line += os.linesep + indentStr + '| ' + \
941  (len(prefix) - len(indentStr) - 3) * ' '
942  line += ' (default: %s)' % (strDef,)
943  # add the line to the total string
944  rep += line + os.linesep
945  # print out full private configurables
946 # if isinstance(v,Configurable) and not v.isPublic():
947 ## rep += v.__str__( indent + 1 ) + os.linesep
948 # elif isinstance(v,GaudiHandleArray):
949 # for vi in v:
950 # if isinstance(vi,Configurable) and not vi.isPublic():
951 ## rep += vi.__str__( indent + 1 ) + os.linesep
952 
953  # print configurables + their properties, or loop over sequence
954 # for cfg in self.__children:
955  for cfg in self.getAllChildren():
956  rep += cfg.__str__(indent + 1, '|=') + os.linesep
957 
958  # print line to easily see end-of-configurable. Note: No linesep!
959  rep += Configurable._printFooter(indentStr, title)
960  return rep
961 
962  def isApplicable(self):
963  '''
964  Return True is the instance can be "applied".
965  Always False for plain Configurable instances
966  (i.e. not ConfigurableUser).
967  '''
968  return False
969 
970 # classes for generic Gaudi component ===========
971 
972 
973 class DummyDescriptor(object):
974  def __init__(self, name):
975  self.__name__ = name # conventional
976 
977  def __get__(self, obj, type=None):
978  return getattr(obj, self.__name__)
979 
980  def __set__(self, obj, value):
981  object.__setattr__(obj, self.__name__, value)
982 
983 
985  #__slots__ = { }
986 
987  def __init__(self, name=Configurable.DefaultName):
988  Configurable.__init__(self, name)
989  self._name = name
990  self._properties = {}
991 
992  def __deepcopy__(self, memo):
993  return self # algorithms are always shared
994 
995  def getGaudiType(self): return 'GenericComponent'
996 
997  def getDlls(self): pass
998 
999  def getHandle(self): pass
1000 
1001  def __setattr__(self, name, value):
1002  # filter private (user) variables
1003  if name[0] == '_':
1004  super(ConfigurableGeneric, self).__setattr__(name, value)
1005  return
1006 
1007  # filter configurable types
1008  if isinstance(value, Configurable):
1009  self.__dict__[name] = value
1010  return
1011 
1012  # assume all the rest are properties
1013  if not name in self._properties:
1014  self._properties[name] = PropertyProxy(DummyDescriptor(name))
1015  self._properties[name].__set__(self, value)
1016 
1017  def getJobOptName(self): return None
1018 
1019 
1020 # base classes for individual Gaudi algorithms/services/algtools ===========
1021 class ConfigurableAlgorithm(Configurable):
1022  __slots__ = {'_jobOptName': 0, 'OutputLevel': 0,
1023  'Enable': 1, 'ErrorMax': 1, 'ErrorCount': 0, 'AuditAlgorithms': 0,
1024  'AuditInitialize': 0, 'AuditReinitialize': 0, 'AuditExecute': 0,
1025  'AuditFinalize': 0, 'AuditBeginRun': 0, 'AuditEndRun': 0}
1026 
1027  def __init__(self, name=Configurable.DefaultName):
1028  super(ConfigurableAlgorithm, self).__init__(name)
1029  name = self.getName()
1030  self._jobOptName = name[name.find('/') + 1:] # strips class
1031 
1032  def __deepcopy__(self, memo):
1033  return self # algorithms are always shared
1034 
1035  def getHandle(self):
1036  return iAlgorithm(self.getJobOptName())
1037 
1038  def getGaudiType(self):
1039  return 'Algorithm'
1040 
1041  def getJobOptName(self):
1042  return self._jobOptName
1043 
1044  # mimick the ControlFlowLeaf interface
1045  def __and__(self, rhs):
1046  if rhs is CFTrue:
1047  return self
1048  elif rhs is CFFalse:
1049  return CFFalse
1050  return AndNode(self, rhs)
1051 
1052  def __or__(self, rhs):
1053  if rhs is CFFalse:
1054  return self
1055  elif rhs is CFTrue:
1056  return CFTrue
1057  return OrNode(self, rhs)
1058 
1059  def __invert__(self):
1060  return InvertNode(self)
1061 
1062  def __rshift__(self, rhs):
1063  return OrderedNode(self, rhs)
1064 
1065  def visitNode(self, visitor):
1066  visitor.enter(self)
1067  self._visitSubNodes(visitor)
1068  visitor.leave(self)
1069 
1070  def _visitSubNodes(self, visitor):
1071  pass
1072 
1073  def __eq__(self, other):
1074  return (repr(self) == repr(other))
1075 
1076 
1078  __slots__ = {'OutputLevel': 0,
1079  'AuditServices': 0, 'AuditInitialize': 0, 'AuditFinalize': 0}
1080 
1081  def __deepcopy__(self, memo):
1082  return self # services are always shared
1083 
1084  def copyChild(self, child):
1085  return child # full sharing
1086 
1087  def getHandle(self):
1088  return iService(self._name)
1089 
1090  def getGaudiType(self):
1091  return 'Service'
1092 
1093  def getGaudiHandle(self):
1094  return ServiceHandle(self.toStringProperty())
1095 
1096  def toStringProperty(self):
1097  # called on conversion to a string property for the jocat
1098  return self.getName()
1099 
1100 
1102  __slots__ = {'_jobOptName': '', 'OutputLevel': 0,
1103  'AuditTools': 0, 'AuditInitialize': 0, 'AuditFinalize': 0}
1104 
1105  def __init__(self, name=Configurable.DefaultName):
1106  super(ConfigurableAlgTool, self).__init__(name)
1107  if '.' not in self._name:
1108  # Public tools must have ToolSvc as parent
1109  self._name = "ToolSvc." + self._name
1110  name = self.getName()
1111  name = name[name.find('/') + 1:] # strips class, if any
1112  self._jobOptName = name
1113 
1114  def getHandle(self):
1115  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1116  return iProperty(self.getJobOptName())
1117 
1118  def getGaudiType(self):
1119  return 'AlgTool'
1120 
1121  def getGaudiHandle(self):
1122  if self.isPublic():
1123  return PublicToolHandle(self.toStringProperty())
1124  else:
1125  return PrivateToolHandle(self.toStringProperty())
1126 
1127  def getPrintTitle(self):
1128  if self.isPublic():
1129  pop = 'Public '
1130  else:
1131  pop = 'Private '
1132  return pop + Configurable.getPrintTitle(self)
1133 
1134  def setParent(self, parentName):
1135  # print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
1136  # print "Calling stack:"
1137  # import traceback
1138  # traceback.print_stack()
1139  # propagate parent to AlgTools in children
1140  for c in self.getAllChildren():
1141  if isinstance(c, ConfigurableAlgTool):
1142  c.setParent(parentName)
1143 
1144  # update my own parent
1145  name = self.getName()
1146  name = name[name.rfind('.') + 1:] # Name of the instance
1147  self._jobOptName = self._name = parentName + '.' + name
1148 
1149  def getParent(self):
1150  dot = self._jobOptName.rfind('.')
1151  if dot != -1:
1152  return self._jobOptName[:dot]
1153  else:
1154  return ""
1155 
1156  def hasParent(self, parent):
1157  return self._jobOptName.startswith(parent + '.')
1158 
1159  def getJobOptName(self):
1160  return self._jobOptName
1161 
1162  def isPublic(self):
1163  return self.isInToolSvc()
1164 
1165  def isInToolSvc(self):
1166  return self._jobOptName.startswith('ToolSvc.')
1167 
1168  def toStringProperty(self):
1169  # called on conversion to a string property for the jocat
1170  return self.getFullName()
1171 
1172  def getFullName(self):
1173  # for Tools, the "full name" means "Type/LocalName",
1174  # without the names of the parents
1175  name = self.getName()
1176  # strip off everything before the last '.'
1177  name = name[name.rfind('.') + 1:]
1178  return str(self.getType() + '/' + name)
1179 
1180 
1181 # FIXME: this is just a placeholder, waiting for a real implementation
1182 # It is sufficient to get us going... (and import a PkgConf which
1183 # happens to contain an Auditor...)
1185  __slots__ = {'_jobOptName': 0, 'OutputLevel': 0,
1186  'Enable': 1}
1187 
1188  def __init__(self, name=Configurable.DefaultName):
1189  super(ConfigurableAuditor, self).__init__(name)
1190  name = self.getName()
1191  name = name[name.find('/') + 1:] # strips class, if any
1192  self._jobOptName = name
1193 
1194  def getHandle(self):
1195  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1196  return iProperty(self.getJobOptName())
1197 
1198  def getGaudiType(self):
1199  return 'Auditor'
1200 
1201  def getJobOptName(self):
1202  return self._jobOptName
1203 
1204  def toStringProperty(self):
1205  # called on conversion to a string property for the jocat
1206  return self.getType() + '/' + self.getName()
1207 
1208 
1210  __slots__ = {"__users__": [],
1211  "__used_instances__": [],
1212  "_enabled": True,
1213  "_applied": False}
1214  # list of ConfigurableUser classes this one is going to modify in the
1215  # __apply_configuration__ method.
1216  # The list may contain class objects, strings representing class objects or
1217  # tuples with the class object (or a string) as first element and the instance
1218  # name as second element.
1219  # If the instance name is None or not present, the function _instanceName()
1220  # is used to determine the name of the instance (the default implementation
1221  # returns "<this name>_<other name>".
1222  __used_configurables__ = []
1223  # list of ConfigurableUser classes this one is going to query in the
1224  # __apply_configuration__ method
1225  __queried_configurables__ = []
1226 
1227  def __init__(self, name=Configurable.DefaultName, _enabled=True, **kwargs):
1228  super(ConfigurableUser, self).__init__(name)
1229  for n, v in kwargs.items():
1230  setattr(self, n, v)
1231  self._enabled = _enabled
1232  self.__users__ = []
1233  self._applied = False
1234 
1235  # Needed to retrieve the actual class if the declaration in __used_configurables__
1236  # and __queried_configurables__ is done with strings.
1237  from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
1238 
1239  # Set the list of users of the used configurables
1240  #
1242  for used in self.__used_configurables__:
1243  # By default we want to use the default name of the instances
1244  # for the used configurables
1245  used_name = Configurable.DefaultName
1246  # If the entry in the list is a tuple, we need a named instance
1247  if type(used) is tuple:
1248  used, used_name = used # we re-set used to re-use the code below
1249  if not used_name:
1250  used_name = self._instanceName(used)
1251  # Check is 'used' is a string or not
1252  if type(used) is str:
1253  used_class = confDbGetConfigurable(used)
1254  else:
1255  used_class = used
1256  # Instantiate the configurable that we are going to use
1257  try:
1258  inst = used_class(name=used_name, _enabled=False)
1259  except AttributeError:
1260  # This cover the case where the used configurable is not a
1261  # ConfigurableUser instance, i.e. id doesn't have the attribute
1262  # '_enabled'.
1263  inst = used_class(name=used_name)
1264  self.__addActiveUseOf(inst)
1265  for queried in self.__queried_configurables__:
1266  try:
1267  if type(queried) is str:
1268  queried = confDbGetConfigurable(used)
1269  inst = queried(_enabled=False)
1270  except AttributeError:
1271  inst = queried()
1272  self.__addPassiveUseOf(inst)
1273 
1274  def __addActiveUseOf(self, other):
1275  """
1276  Declare that we are going to modify the Configurable 'other' in our
1277  __apply_configuration__.
1278  """
1279  self.__used_instances__.append(other)
1280  if hasattr(other, "__users__"): # allow usage of plain Configurables
1281  other.__users__.append(self)
1282 
1283  def __addPassiveUseOf(self, other):
1284  """
1285  Declare that we are going to retrieve property values from the
1286  ConfigurableUser 'other' in our __apply_configuration__.
1287  """
1288  if not isinstance(other, ConfigurableUser):
1289  raise Error("'%s': Cannot make passive use of '%s', it is not a ConfigurableUser" % (
1290  self.name(), other.name()))
1291  other.__addActiveUseOf(self)
1292 
1293  def getGaudiType(self):
1294  return 'User'
1295 
1296  def getDlls(self):
1297  return None
1298 
1299  def getHandle(self):
1300  return None
1301 
1302  def __detach_used__(self):
1303  """
1304  Remove this ConfigurableUser instance from the users list of the used
1305  instances.
1306  """
1307  for used in self.__used_instances__:
1308  if hasattr(used, "__users__"): # allow usage of plain Configurables
1309  used.__users__.remove(self)
1310 
1311  def propagateProperty(self, name, others=None, force=True):
1312  """
1313  Propagate the property 'name' (if set) to other configurables (if possible).
1314  'others' can be:
1315  None:
1316  propagate to all the entries in __used_configurables__
1317  a configurable instance:
1318  propagate only to it
1319  list of configurable instances:
1320  propagate to all of them.
1321 
1322 
1323  The logic is:
1324  - if the local property is set, the other property will be overwritten
1325  - local property not set and other set => keep other
1326  - local property not set and other not set => overwrite the default for
1327  ConfigurableUser instances and set the property for Configurables
1328  """
1329  # transform 'others' to a list of configurable instances
1330  if others is None:
1331  others = self.__used_instances__
1332  elif type(others) not in [list, tuple]:
1333  others = [others]
1334  # these can be computed before the loop
1335  local_is_set = self.isPropertySet(name)
1336  value = self.getProp(name)
1337  # loop over the others that do have 'name' in their slots
1338  for other in [o for o in others if name in o.__slots__]:
1339  # If self property is set, use it
1340  if local_is_set:
1341  if other.isPropertySet(name):
1342  log.warning("Property '%(prop)s' is set in both '%(self)s' and '%(other)s', using '%(self)s.%(prop)s'" %
1343  {"self": self.name(),
1344  "other": other.name(),
1345  "prop": name})
1346  other.setProp(name, value)
1347  # If not, and other property also not set, propagate the default
1348  elif not other.isPropertySet(name):
1349  if isinstance(other, ConfigurableUser):
1350  otherType = type(other._properties[name].getDefault())
1351  other._properties[name].setDefault(value)
1352  if otherType in [list, dict]:
1353  # Special case for list and dictionaries:
1354  # also set the property to the same value of the default (copy)
1355  other.setProp(name, otherType(value))
1356  else:
1357  other.setProp(name, value)
1358  # If not set and other set, do nothing
1359 
1360  def propagateProperties(self, names=None, others=None, force=True):
1361  """
1362  Call propagateProperty for each property listed in 'names'.
1363  If 'names' is None, all the properties are propagated.
1364  """
1365  if names is None:
1366  # use all the non-private slots
1367  names = [p for p in self.__slots__ if not p.startswith("_")]
1368  for n in names:
1369  self.propagateProperty(n, others, force)
1370 
1372  """
1373  Function to be overridden to convert the high level configuration into a
1374  low level one.
1375  The default implementation calls applyConf, which is the method defined
1376  in some ConfigurableUser implementations.
1377  """
1378  return self.applyConf()
1379 
1380  def applyConf(self):
1381  """
1382  Function to be overridden to convert the high level configuration into a
1383  low level one.
1384  """
1385  pass
1386 
1387  def _instanceName(self, cls):
1388  """
1389  Function used to define the name of the private instance of a given class
1390  name.
1391  This method is used when the __used_configurables_property__ declares the
1392  need of a private used configurable without specifying the name.
1393  """
1394  if type(cls) is str:
1395  clName = cls
1396  else:
1397  clName = cls.__name__
1398  return "%s_%s" % (self.name(), clName)
1399 
1400  def getUsedInstance(self, name):
1401  """
1402  Return the used instance with a given name.
1403  """
1404  for i in self.__used_instances__:
1405  if i.name() == name:
1406  if hasattr(i, "_enabled"):
1407  # ensure that the instances retrieved through the method are
1408  # enabled
1409  i._enabled = True
1410  return i
1411  raise KeyError(name)
1412 
1413  def isApplicable(self):
1414  '''
1415  Return True is the instance can be "applied".
1416  '''
1417  return (not self.__users__) and (not self._applied)
1418 
1419 
1420 # list of callables to be called after all the __apply_configuration__ are called.
1421 postConfigActions = []
1422 
1423 
1425  """
1426  Add a new callable ('function') to the list of post-configuration actions.
1427  If the callable is already in the list, it is moved to the end of the list.
1428  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1429  """
1430  try:
1431  postConfigActions.remove(function)
1432  except:
1433  pass
1434  postConfigActions.append(function)
1435 
1436 
1438  """
1439  Remove a callable from the list of post-config actions.
1440  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1441  """
1442  postConfigActions.remove(function)
1443 
1444 
1445 _appliedConfigurableUsers_ = False
1446 
1447 
1449  """
1450  Call the apply method of all the ConfigurableUser instances respecting the
1451  dependencies. First the C.U.s that are not used by anybody, then the used
1452  ones, when they are not used anymore.
1453  """
1454  # Avoid double calls
1455  global _appliedConfigurableUsers_, postConfigActions
1456  if _appliedConfigurableUsers_:
1457  return
1458  _appliedConfigurableUsers_ = True
1459 
1460  def applicableConfUsers():
1461  '''
1462  Generator returning all the configurables that can be applied in the
1463  order in which they can be applied.
1464  '''
1465  # This is tricky...
1466  # We keep on looking for the first configurable that can be applied.
1467  # When we cannot find any, 'next()' raises a StopIteration that is
1468  # propagated outside of the infinite loop and the function, then handled
1469  # correctly from outside (it is the exception that is raised when you
1470  # exit from a generator).
1471  # Checking every time the full list is inefficient, but it is the
1472  # easiest way to fix bug #103803.
1473  # <https://savannah.cern.ch/bugs/?103803>
1474  while True:
1475  yield (c for c in Configurable.allConfigurables.values()
1476  if c.isApplicable()).next()
1477 
1478  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1479  for c in applicableConfUsers():
1480  if c._enabled:
1481  log.info("applying configuration of %s", c.name())
1482  if debugApplyOrder:
1483  sys.stderr.write('applying %r' % c)
1484  c.__apply_configuration__()
1485  log.info(c)
1486  else:
1487  log.info("skipping configuration of %s", c.name())
1488  c._applied = True # flag the instance as already applied
1489  if hasattr(c, "__detach_used__"):
1490  # tells the used configurables that they are not needed anymore
1491  c.__detach_used__()
1492 
1493  # check for dependency loops
1494  leftConfUsers = [c for c in Configurable.allConfigurables.values()
1495  if hasattr(c, '__apply_configuration__') and
1496  c._enabled and not c._applied]
1497  # if an enabled configurable has not been applied, there must be a dependency loop
1498  if leftConfUsers:
1499  raise Error("Detected loop in the ConfigurableUser"
1500  " dependencies: %r" % [c.name()
1501  for c in leftConfUsers])
1502  # ensure that all the Handles have been triggered
1503  known = set()
1504  unknown = set(Configurable.allConfigurables)
1505  while unknown:
1506  for k in unknown:
1507  if not known: # do not print during the first iteration
1508  log.debug('new configurable created automatically: %s', k)
1509  # this trigger the instantiation from handles
1510  Configurable.allConfigurables[k].properties()
1511  known.add(k)
1512  unknown -= known
1513  # Call post-config actions
1514  for action in postConfigActions:
1515  action()
1516 
1517 
1519  """
1520  Obsolete (buggy) implementation of applyConfigurableUsers(), kept to provide
1521  backward compatibility for configurations that where relying (implicitly) on
1522  bug #103803, or on a specific (non guaranteed) order of execution.
1523 
1524  @see applyConfigurableUsers()
1525  """
1526  # Avoid double calls
1527  global _appliedConfigurableUsers_, postConfigActions
1528  if _appliedConfigurableUsers_:
1529  return
1530  _appliedConfigurableUsers_ = True
1531 
1532  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1533  confUsers = [c
1534  for c in Configurable.allConfigurables.values()
1535  if hasattr(c, "__apply_configuration__")]
1536  applied = True # needed to detect dependency loops
1537  while applied and confUsers:
1538  newConfUsers = [] # list of conf users that cannot be applied yet
1539  applied = False
1540  for c in confUsers:
1541  if hasattr(c, "__users__") and c.__users__:
1542  newConfUsers.append(c) # cannot use this one yet
1543  else: # it does not have users or the list is empty
1544  applied = True
1545  # the ConfigurableUser is enabled if it doesn't have an _enabled
1546  # property or its value is True
1547  enabled = (not hasattr(c, "_enabled")) or c._enabled
1548  if enabled:
1549  log.info("applying configuration of %s", c.name())
1550  if debugApplyOrder:
1551  sys.stderr.write('applying %r' % c)
1552  c.__apply_configuration__()
1553  log.info(c)
1554  else:
1555  log.info("skipping configuration of %s", c.name())
1556  if hasattr(c, "__detach_used__"):
1557  # tells the used configurables that they are not needed anymore
1558  c.__detach_used__()
1559  confUsers = newConfUsers # list of C.U.s still to go
1560  if confUsers:
1561  # this means that some C.U.s could not be applied because of a dependency loop
1562  raise Error("Detected loop in the ConfigurableUser "
1563  " dependencies: %r" % [c.name()
1564  for c in confUsers])
1565  # ensure that all the Handles have been triggered
1566  known = set()
1567  unknown = set(Configurable.allConfigurables)
1568  while unknown:
1569  for k in unknown:
1570  if not known: # do not print during the first iteration
1571  log.debug('new configurable created automatically: %s', k)
1572  # this trigger the instantiation from handles
1573  Configurable.allConfigurables[k].properties()
1574  known.add(k)
1575  unknown -= known
1576  # Call post-config actions
1577  for action in postConfigActions:
1578  action()
1579 
1580 
1582  """
1583  Function to select all and only the configurables that have to be used in
1584  GaudiPython.AppMgr constructor.
1585  This is needed because in Athena the implementation have to be different (the
1586  configuration is used in a different moment).
1587  """
1588  return [k
1589  for k, v in Configurable.allConfigurables.items()
1590  if v.getGaudiType() != "User"] # Exclude ConfigurableUser instances
1591 
1592 
1593 def purge():
1594  """
1595  Clean up all configurations and configurables.
1596  """
1597  for c in Configurable.allConfigurables.values():
1598  c.__class__.configurables.clear()
1599  Configurable.allConfigurables.clear()
1600  # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
1601  # migrated to the correct class when this is known.
1602  ConfigurableGeneric.configurables.clear()
1603  from ProcessJobOptions import _included_files
1604  import os.path
1605  import sys
1606  for file in _included_files:
1607  dirname, basname = os.path.split(file)
1608  basname, ext = os.path.splitext(basname)
1609  if basname in sys.modules:
1610  del sys.modules[basname]
1611  _included_files.clear()
1612 
1613 
1615  def __init__(self):
1616  self.stack = []
1617 
1618  @property
1619  def sequence(self):
1620  return self.stack[-1]
1621 
1622  def enter(self, visitee):
1623  pass
1624 
1625  def _getUniqueName(self, prefix):
1626  from Gaudi.Configuration import allConfigurables
1627  cnt = 0
1628  name = prefix + str(cnt)
1629  while name in allConfigurables:
1630  cnt += 1
1631  name = prefix + str(cnt)
1632  return name
1633 
1634  def _newSeq(self, prefix='seq_', **kwargs):
1635  from Configurables import GaudiSequencer
1636  return GaudiSequencer(self._getUniqueName('seq_'),
1637  **kwargs)
1638 
1639  def leave(self, visitee):
1640  stack = self.stack
1641  if visitee in (CFTrue, CFFalse):
1642  stack.append(self._newSeq(Invert=visitee is CFFalse))
1643  elif isinstance(visitee, (ControlFlowLeaf, ConfigurableAlgorithm)):
1644  stack.append(visitee)
1645  elif isinstance(visitee, (OrNode, AndNode, OrderedNode)):
1646  b = stack.pop()
1647  a = stack.pop()
1648  seq = self._newSeq(Members=[a, b],
1649  ModeOR=isinstance(visitee, OrNode),
1650  ShortCircuit=not isinstance(
1651  visitee, OrderedNode),
1652  MeasureTime=True)
1653  stack.append(seq)
1654  elif isinstance(visitee, ignore):
1655  if hasattr(stack[-1], 'IgnoreFilterPassed'):
1656  stack[-1].IgnoreFilterPassed = True
1657  else:
1658  stack.append(self._newSeq(Members=[stack.pop()],
1659  IgnoreFilterPassed=True))
1660  elif isinstance(visitee, InvertNode):
1661  if hasattr(stack[-1], 'Invert'):
1662  stack[-1].Invert = True
1663  else:
1664  stack.append(self._newSeq(Members=[stack.pop()],
1665  Invert=True))
1666 
1667 
1668 def makeSequences(expression):
1669  '''
1670  Convert a control flow expression to nested GaudiSequencers.
1671  '''
1672  if not isinstance(expression, ControlFlowNode):
1673  raise ValueError('ControlFlowNode instance expected, got %s' %
1674  type(expression).__name__)
1675  visitor = CreateSequencesVisitor()
1676  expression.visitNode(visitor)
1677  return visitor.sequence
1678 
1679 
1680 class SuperAlgorithm(ControlFlowNode):
1681  '''
1682  Helper class to use a ControlFlowNode as an algorithm configurable
1683  instance.
1684  '''
1685  def __new__(cls, name=None, **kwargs):
1686  if name is None:
1687  name = cls.__name__
1688  if name in Configurable.allConfigurables:
1689  instance = Configurable.allConfigurables[name]
1690  assert type(instance) is cls, \
1691  ('trying to reuse {0!r} as name of a {1} instance while it''s '
1692  'already used for an instance of {2}').format(
1693  name,
1694  cls.__name__,
1695  type(instance).__name__)
1696  return instance
1697  else:
1698  instance = super(SuperAlgorithm, cls).__new__(cls, name, **kwargs)
1699  Configurable.allConfigurables[name] = instance
1700  return instance
1701 
1702  def __init__(self, name=None, **kwargs):
1703  self._name = name or self.getType()
1704  self.graph = self._initGraph()
1705  for key in kwargs:
1706  setattr(self, key, kwargs[key])
1707 
1708  @property
1709  def name(self): # name is a read only property
1710  return self._name
1711 
1712  @classmethod
1713  def getType(cls):
1714  return cls.__name__
1715 
1716  # required to be registered in allConfigurables
1717  def properties(self):
1718  pass
1719 
1720  # required to be registered in allConfigurables
1721  def isApplicable(self):
1722  return False
1723 
1724  # required to be registered in allConfigurables
1725  def getGaudiType(self):
1726  return 'User'
1727 
1728  def _makeAlg(self, typ, **kwargs):
1729  '''
1730  Instantiate and algorithm of type 'typ' with a name suitable for use
1731  inside a SuperAlgorithm.
1732  '''
1733  name = '{0}_{1}'.format(self.name, kwargs.pop('name', typ.getType()))
1734  return typ(name, **kwargs)
1735 
1736  def _initGraph(self):
1737  raise NotImplementedError()
1738 
1739  def __repr__(self):
1740  return '{0}({1!r})'.format(self.getType(), self.name)
1741 
1742  def _visitSubNodes(self, visitor):
1743  if self.graph:
1744  self.graph.visitNode(visitor)
1745 
1746  def __setattr__(self, name, value):
1747  super(SuperAlgorithm, self).__setattr__(name, value)
1748  if name in ('_name', 'graph'):
1749  # do not propagate internal data members
1750  return
1751 
1752  class PropSetter(object):
1753  def enter(self, node):
1754  try:
1755  setattr(node, name, value)
1756  except (ValueError, AttributeError):
1757  # ignore type and name mismatch
1758  pass
1759 
1760  def leave(self, node):
1761  pass
1762 
1763  self._visitSubNodes(PropSetter())
def propagateProperty(self, name, others=None, force=True)
def _newSeq(self, prefix='seq_', kwargs)
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:120
def _printHeader(indentStr, title)
def __init__(self, name=Configurable.DefaultName, _enabled=True, kwargs)
def __init__(self, name=None, kwargs)
def appendPostConfigAction(function)
Sequencer for executing several algorithms, stopping when one is faulty.
def __init__(self, name=DefaultName)
def addTool(self, tool, name=None)
def makeSequences(expression)
def _printFooter(indentStr, title)
def __init__(self, name=Configurable.DefaultName)
def clone(self, name=None, kwargs)
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:304
def __init__(self, name=Configurable.DefaultName)
def __init__(self, name=Configurable.DefaultName)
def copyChildAndSetParent(self, cfg, parent)
def __iadd__(self, configs, descr=None)
def __new__(cls, name=None, kwargs)
def removePostConfigAction(function)
def __str__(self, indent=0, headerLastIndentUnit=indentUnit)
def __init__(self, name=Configurable.DefaultName)
def propagateProperties(self, names=None, others=None, force=True)
def __get__(self, obj, type=None)
def __setattr__(self, name, value)
def isApplicable(self)
rep += v.__str__( indent + 1 ) + os.linesep elif isinstance(v,GaudiHandleArray): for vi in v: if isin...