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