The Gaudi Framework  v32r1 (f65d50dc)
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 
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  @classmethod
1032  def getGaudiType(cls):
1033  return 'GenericComponent'
1034 
1035  def getDlls(self):
1036  pass
1037 
1038  def getHandle(self):
1039  pass
1040 
1041  def __setattr__(self, name, value):
1042  # filter private (user) variables
1043  if name[0] == '_':
1044  super(ConfigurableGeneric, self).__setattr__(name, value)
1045  return
1046 
1047  # filter configurable types
1048  if isinstance(value, Configurable):
1049  self.__dict__[name] = value
1050  return
1051 
1052  # assume all the rest are properties
1053  if not name in self._properties:
1054  self._properties[name] = PropertyProxy(DummyDescriptor(name))
1055  self._properties[name].__set__(self, value)
1056 
1057  def getJobOptName(self):
1058  return None
1059 
1060 
1061 # base classes for individual Gaudi algorithms/services/algtools ===========
1063  __slots__ = {
1064  '_jobOptName': 0,
1065  'OutputLevel': 0,
1066  'Enable': 1,
1067  'ErrorMax': 1,
1068  'ErrorCount': 0,
1069  'AuditAlgorithms': 0,
1070  'AuditInitialize': 0,
1071  'AuditReinitialize': 0,
1072  'AuditExecute': 0,
1073  'AuditFinalize': 0,
1074  'AuditBeginRun': 0,
1075  'AuditEndRun': 0
1076  }
1077 
1078  def __init__(self, name=Configurable.DefaultName):
1079  super(ConfigurableAlgorithm, self).__init__(name)
1080  name = self.getName()
1081  self._jobOptName = name[name.find('/') + 1:] # strips class
1082 
1083  def __deepcopy__(self, memo):
1084  return self # algorithms are always shared
1085 
1086  def getHandle(self):
1087  return iAlgorithm(self.getJobOptName())
1088 
1089  @classmethod
1090  def getGaudiType(cls):
1091  return 'Algorithm'
1092 
1093  def getJobOptName(self):
1094  return self._jobOptName
1095 
1096  # mimick the ControlFlowLeaf interface
1097  def __and__(self, rhs):
1098  if rhs is CFTrue:
1099  return self
1100  elif rhs is CFFalse:
1101  return CFFalse
1102  return AndNode(self, rhs)
1103 
1104  def __or__(self, rhs):
1105  if rhs is CFFalse:
1106  return self
1107  elif rhs is CFTrue:
1108  return CFTrue
1109  return OrNode(self, rhs)
1110 
1111  def __invert__(self):
1112  return InvertNode(self)
1113 
1114  def __rshift__(self, rhs):
1115  return OrderedNode(self, rhs)
1116 
1117  def visitNode(self, visitor):
1118  visitor.enter(self)
1119  self._visitSubNodes(visitor)
1120  visitor.leave(self)
1121 
1122  def _visitSubNodes(self, visitor):
1123  pass
1124 
1125  def __eq__(self, other):
1126  return (repr(self) == repr(other))
1127 
1128 
1130  __slots__ = {
1131  'OutputLevel': 0,
1132  'AuditServices': 0,
1133  'AuditInitialize': 0,
1134  'AuditFinalize': 0
1135  }
1136 
1137  def __deepcopy__(self, memo):
1138  return self # services are always shared
1139 
1140  def copyChild(self, child):
1141  return child # full sharing
1142 
1143  def getHandle(self):
1144  return iService(self._name)
1145 
1146  @classmethod
1147  def getGaudiType(cls):
1148  return 'Service'
1149 
1150  def getGaudiHandle(self):
1151  return ServiceHandle(self.toStringProperty())
1152 
1153  def toStringProperty(self):
1154  # called on conversion to a string property for the jocat
1155  return self.getName()
1156 
1157 
1159  __slots__ = {
1160  '_jobOptName': '',
1161  'OutputLevel': 0,
1162  'AuditTools': 0,
1163  'AuditInitialize': 0,
1164  'AuditFinalize': 0
1165  }
1166 
1167  def __init__(self, name=Configurable.DefaultName):
1168  super(ConfigurableAlgTool, self).__init__(name)
1169  if '.' not in self._name:
1170  # Public tools must have ToolSvc as parent
1171  self._name = "ToolSvc." + self._name
1172  name = self.getName()
1173  name = name[name.find('/') + 1:] # strips class, if any
1174  self._jobOptName = name
1175 
1176  def getHandle(self):
1177  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1178  return iProperty(self.getJobOptName())
1179 
1180  @classmethod
1181  def getGaudiType(cls):
1182  return 'AlgTool'
1183 
1184  def getGaudiHandle(self):
1185  if self.isPublic():
1186  return PublicToolHandle(self.toStringProperty())
1187  else:
1188  return PrivateToolHandle(self.toStringProperty())
1189 
1190  def getPrintTitle(self):
1191  if self.isPublic():
1192  pop = 'Public '
1193  else:
1194  pop = 'Private '
1195  return pop + Configurable.getPrintTitle(self)
1196 
1197  def setParent(self, parentName):
1198  # print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
1199  # print "Calling stack:"
1200  # import traceback
1201  # traceback.print_stack()
1202  # propagate parent to AlgTools in children
1203  for c in self.getAllChildren():
1204  if isinstance(c, ConfigurableAlgTool):
1205  c.setParent(parentName)
1206 
1207  # update my own parent
1208  name = self.getName()
1209  name = name[name.rfind('.') + 1:] # Name of the instance
1210  self._jobOptName = self._name = parentName + '.' + name
1211 
1212  def getParent(self):
1213  dot = self._jobOptName.rfind('.')
1214  if dot != -1:
1215  return self._jobOptName[:dot]
1216  else:
1217  return ""
1218 
1219  def hasParent(self, parent):
1220  return self._jobOptName.startswith(parent + '.')
1221 
1222  def getJobOptName(self):
1223  return self._jobOptName
1224 
1225  def isPublic(self):
1226  return self.isInToolSvc()
1227 
1228  def isInToolSvc(self):
1229  return self._jobOptName.startswith('ToolSvc.')
1230 
1231  def toStringProperty(self):
1232  # called on conversion to a string property for the jocat
1233  return self.getFullName()
1234 
1235  def getFullName(self):
1236  # for Tools, the "full name" means "Type/LocalName",
1237  # without the names of the parents
1238  name = self.getName()
1239  # strip off everything before the last '.'
1240  name = name[name.rfind('.') + 1:]
1241  return str(self.getType() + '/' + name)
1242 
1243 
1244 # FIXME: this is just a placeholder, waiting for a real implementation
1245 # It is sufficient to get us going... (and import a PkgConf which
1246 # happens to contain an Auditor...)
1248  __slots__ = {'_jobOptName': 0, 'OutputLevel': 0, 'Enable': 1}
1249 
1250  def __init__(self, name=Configurable.DefaultName):
1251  super(ConfigurableAuditor, self).__init__(name)
1252  name = self.getName()
1253  name = name[name.find('/') + 1:] # strips class, if any
1254  self._jobOptName = name
1255 
1256  def getHandle(self):
1257  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1258  return iProperty(self.getJobOptName())
1259 
1260  @classmethod
1261  def getGaudiType(cls):
1262  return 'Auditor'
1263 
1264  def getJobOptName(self):
1265  return self._jobOptName
1266 
1267  def toStringProperty(self):
1268  # called on conversion to a string property for the jocat
1269  return self.getType() + '/' + self.getName()
1270 
1271 
1273  __slots__ = {
1274  "__users__": [],
1275  "__used_instances__": [],
1276  "_enabled": True,
1277  "_applied": False
1278  }
1279  # list of ConfigurableUser classes this one is going to modify in the
1280  # __apply_configuration__ method.
1281  # The list may contain class objects, strings representing class objects or
1282  # tuples with the class object (or a string) as first element and the instance
1283  # name as second element.
1284  # If the instance name is None or not present, the function _instanceName()
1285  # is used to determine the name of the instance (the default implementation
1286  # returns "<this name>_<other name>".
1287  __used_configurables__ = []
1288  # list of ConfigurableUser classes this one is going to query in the
1289  # __apply_configuration__ method
1290  __queried_configurables__ = []
1291 
1292  def __init__(self, name=Configurable.DefaultName, _enabled=True, **kwargs):
1293  super(ConfigurableUser, self).__init__(name)
1294  for n, v in kwargs.items():
1295  setattr(self, n, v)
1296  self._enabled = _enabled
1297  self.__users__ = []
1298  self._applied = False
1299 
1300  # Needed to retrieve the actual class if the declaration in __used_configurables__
1301  # and __queried_configurables__ is done with strings.
1302  from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
1303 
1304  # Set the list of users of the used configurables
1305  #
1307  for used in self.__used_configurables__:
1308  # By default we want to use the default name of the instances
1309  # for the used configurables
1310  used_name = Configurable.DefaultName
1311  # If the entry in the list is a tuple, we need a named instance
1312  if type(used) is tuple:
1313  used, used_name = used # we re-set used to re-use the code below
1314  if not used_name:
1315  used_name = self._instanceName(used)
1316  # Check is 'used' is a string or not
1317  if type(used) is str:
1318  used_class = confDbGetConfigurable(used)
1319  else:
1320  used_class = used
1321  # Instantiate the configurable that we are going to use
1322  try:
1323  inst = used_class(name=used_name, _enabled=False)
1324  except AttributeError:
1325  # This cover the case where the used configurable is not a
1326  # ConfigurableUser instance, i.e. id doesn't have the attribute
1327  # '_enabled'.
1328  inst = used_class(name=used_name)
1329  self.__addActiveUseOf(inst)
1330  for queried in self.__queried_configurables__:
1331  try:
1332  if type(queried) is str:
1333  queried = confDbGetConfigurable(used)
1334  inst = queried(_enabled=False)
1335  except AttributeError:
1336  inst = queried()
1337  self.__addPassiveUseOf(inst)
1338 
1339  def __addActiveUseOf(self, other):
1340  """
1341  Declare that we are going to modify the Configurable 'other' in our
1342  __apply_configuration__.
1343  """
1344  self.__used_instances__.append(other)
1345  if hasattr(other, "__users__"): # allow usage of plain Configurables
1346  other.__users__.append(self)
1347 
1348  def __addPassiveUseOf(self, other):
1349  """
1350  Declare that we are going to retrieve property values from the
1351  ConfigurableUser 'other' in our __apply_configuration__.
1352  """
1353  if not isinstance(other, ConfigurableUser):
1354  raise Error(
1355  "'%s': Cannot make passive use of '%s', it is not a ConfigurableUser"
1356  % (self.name(), other.name()))
1357  other.__addActiveUseOf(self)
1358 
1359  @classmethod
1360  def getGaudiType(cls):
1361  return 'User'
1362 
1363  def getDlls(self):
1364  return None
1365 
1366  def getHandle(self):
1367  return None
1368 
1369  def __detach_used__(self):
1370  """
1371  Remove this ConfigurableUser instance from the users list of the used
1372  instances.
1373  """
1374  for used in self.__used_instances__:
1375  if hasattr(used,
1376  "__users__"): # allow usage of plain Configurables
1377  used.__users__.remove(self)
1378 
1379  def propagateProperty(self, name, others=None, force=True):
1380  """
1381  Propagate the property 'name' (if set) to other configurables (if possible).
1382  'others' can be:
1383  None:
1384  propagate to all the entries in __used_configurables__
1385  a configurable instance:
1386  propagate only to it
1387  list of configurable instances:
1388  propagate to all of them.
1389 
1390 
1391  The logic is:
1392  - if the local property is set, the other property will be overwritten
1393  - local property not set and other set => keep other
1394  - local property not set and other not set => overwrite the default for
1395  ConfigurableUser instances and set the property for Configurables
1396  """
1397  # transform 'others' to a list of configurable instances
1398  if others is None:
1399  others = self.__used_instances__
1400  elif type(others) not in [list, tuple]:
1401  others = [others]
1402  # these can be computed before the loop
1403  local_is_set = self.isPropertySet(name)
1404  value = self.getProp(name)
1405  # loop over the others that do have 'name' in their slots
1406  for other in [o for o in others if name in o.__slots__]:
1407  # If self property is set, use it
1408  if local_is_set:
1409  if other.isPropertySet(name):
1410  log.warning(
1411  "Property '%(prop)s' is set in both '%(self)s' and '%(other)s', using '%(self)s.%(prop)s'"
1412  % {
1413  "self": self.name(),
1414  "other": other.name(),
1415  "prop": name
1416  })
1417  other.setProp(name, value)
1418  # If not, and other property also not set, propagate the default
1419  elif not other.isPropertySet(name):
1420  if isinstance(other, ConfigurableUser):
1421  otherType = type(other._properties[name].getDefault())
1422  other._properties[name].setDefault(value)
1423  if otherType in [list, dict]:
1424  # Special case for list and dictionaries:
1425  # also set the property to the same value of the default (copy)
1426  other.setProp(name, otherType(value))
1427  else:
1428  other.setProp(name, value)
1429  # If not set and other set, do nothing
1430 
1431  def propagateProperties(self, names=None, others=None, force=True):
1432  """
1433  Call propagateProperty for each property listed in 'names'.
1434  If 'names' is None, all the properties are propagated.
1435  """
1436  if names is None:
1437  # use all the non-private slots
1438  names = [p for p in self.__slots__ if not p.startswith("_")]
1439  for n in names:
1440  self.propagateProperty(n, others, force)
1441 
1443  """
1444  Function to be overridden to convert the high level configuration into a
1445  low level one.
1446  The default implementation calls applyConf, which is the method defined
1447  in some ConfigurableUser implementations.
1448  """
1449  return self.applyConf()
1450 
1451  def applyConf(self):
1452  """
1453  Function to be overridden to convert the high level configuration into a
1454  low level one.
1455  """
1456  pass
1457 
1458  def _instanceName(self, cls):
1459  """
1460  Function used to define the name of the private instance of a given class
1461  name.
1462  This method is used when the __used_configurables_property__ declares the
1463  need of a private used configurable without specifying the name.
1464  """
1465  if type(cls) is str:
1466  clName = cls
1467  else:
1468  clName = cls.__name__
1469  return "%s_%s" % (self.name(), clName)
1470 
1471  def getUsedInstance(self, name):
1472  """
1473  Return the used instance with a given name.
1474  """
1475  for i in self.__used_instances__:
1476  if i.name() == name:
1477  if hasattr(i, "_enabled"):
1478  # ensure that the instances retrieved through the method are
1479  # enabled
1480  i._enabled = True
1481  return i
1482  raise KeyError(name)
1483 
1484  def isApplicable(self):
1485  '''
1486  Return True is the instance can be "applied".
1487  '''
1488  return (not self.__users__) and (not self._applied)
1489 
1490 
1491 # list of callables to be called after all the __apply_configuration__ are called.
1492 postConfigActions = []
1493 
1494 
1496  """
1497  Add a new callable ('function') to the list of post-configuration actions.
1498  If the callable is already in the list, it is moved to the end of the list.
1499  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1500  """
1501  try:
1502  postConfigActions.remove(function)
1503  except:
1504  pass
1505  postConfigActions.append(function)
1506 
1507 
1509  """
1510  Remove a callable from the list of post-config actions.
1511  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1512  """
1513  postConfigActions.remove(function)
1514 
1515 
1516 _appliedConfigurableUsers_ = False
1517 
1518 
1520  """
1521  Call the apply method of all the ConfigurableUser instances respecting the
1522  dependencies. First the C.U.s that are not used by anybody, then the used
1523  ones, when they are not used anymore.
1524  """
1525  # Avoid double calls
1526  global _appliedConfigurableUsers_, postConfigActions
1527  if _appliedConfigurableUsers_:
1528  return
1529  _appliedConfigurableUsers_ = True
1530 
1531  def applicableConfUsers():
1532  '''
1533  Generator returning all the configurables that can be applied in the
1534  order in which they can be applied.
1535  '''
1536  # This is tricky...
1537  # We keep on looking for the first configurable that can be applied.
1538  # When we cannot find any, 'next()' raises a StopIteration that is
1539  # propagated outside of the infinite loop and the function, then handled
1540  # correctly from outside (it is the exception that is raised when you
1541  # exit from a generator).
1542  # Checking every time the full list is inefficient, but it is the
1543  # easiest way to fix bug #103803.
1544  # <https://savannah.cern.ch/bugs/?103803>
1545  while True:
1546  yield (c for c in Configurable.allConfigurables.values()
1547  if c.isApplicable()).next()
1548 
1549  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1550  for c in applicableConfUsers():
1551  if c._enabled:
1552  log.info("applying configuration of %s", c.name())
1553  if debugApplyOrder:
1554  sys.stderr.write('applying %r' % c)
1555  c.__apply_configuration__()
1556  log.info(c)
1557  else:
1558  log.info("skipping configuration of %s", c.name())
1559  c._applied = True # flag the instance as already applied
1560  if hasattr(c, "__detach_used__"):
1561  # tells the used configurables that they are not needed anymore
1562  c.__detach_used__()
1563 
1564  # check for dependency loops
1565  leftConfUsers = [
1566  c for c in Configurable.allConfigurables.values() if
1567  hasattr(c, '__apply_configuration__') and c._enabled and not c._applied
1568  ]
1569  # if an enabled configurable has not been applied, there must be a dependency loop
1570  if leftConfUsers:
1571  raise Error("Detected loop in the ConfigurableUser"
1572  " dependencies: %r" % [c.name() for c in leftConfUsers])
1573  # ensure that all the Handles have been triggered
1574  known = set()
1575  unknown = set(Configurable.allConfigurables)
1576  while unknown:
1577  for k in unknown:
1578  if not known: # do not print during the first iteration
1579  log.debug('new configurable created automatically: %s', k)
1580  # this trigger the instantiation from handles
1581  Configurable.allConfigurables[k].properties()
1582  known.add(k)
1583  unknown -= known
1584  # Call post-config actions
1585  for action in postConfigActions:
1586  action()
1587 
1588 
1590  """
1591  Obsolete (buggy) implementation of applyConfigurableUsers(), kept to provide
1592  backward compatibility for configurations that where relying (implicitly) on
1593  bug #103803, or on a specific (non guaranteed) order of execution.
1594 
1595  @see applyConfigurableUsers()
1596  """
1597  # Avoid double calls
1598  global _appliedConfigurableUsers_, postConfigActions
1599  if _appliedConfigurableUsers_:
1600  return
1601  _appliedConfigurableUsers_ = True
1602 
1603  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1604  confUsers = [
1605  c for c in Configurable.allConfigurables.values()
1606  if hasattr(c, "__apply_configuration__")
1607  ]
1608  applied = True # needed to detect dependency loops
1609  while applied and confUsers:
1610  newConfUsers = [] # list of conf users that cannot be applied yet
1611  applied = False
1612  for c in confUsers:
1613  if hasattr(c, "__users__") and c.__users__:
1614  newConfUsers.append(c) # cannot use this one yet
1615  else: # it does not have users or the list is empty
1616  applied = True
1617  # the ConfigurableUser is enabled if it doesn't have an _enabled
1618  # property or its value is True
1619  enabled = (not hasattr(c, "_enabled")) or c._enabled
1620  if enabled:
1621  log.info("applying configuration of %s", c.name())
1622  if debugApplyOrder:
1623  sys.stderr.write('applying %r' % c)
1624  c.__apply_configuration__()
1625  log.info(c)
1626  else:
1627  log.info("skipping configuration of %s", c.name())
1628  if hasattr(c, "__detach_used__"):
1629  # tells the used configurables that they are not needed anymore
1630  c.__detach_used__()
1631  confUsers = newConfUsers # list of C.U.s still to go
1632  if confUsers:
1633  # this means that some C.U.s could not be applied because of a dependency loop
1634  raise Error("Detected loop in the ConfigurableUser "
1635  " dependencies: %r" % [c.name() for c in confUsers])
1636  # ensure that all the Handles have been triggered
1637  known = set()
1638  unknown = set(Configurable.allConfigurables)
1639  while unknown:
1640  for k in unknown:
1641  if not known: # do not print during the first iteration
1642  log.debug('new configurable created automatically: %s', k)
1643  # this trigger the instantiation from handles
1644  Configurable.allConfigurables[k].properties()
1645  known.add(k)
1646  unknown -= known
1647  # Call post-config actions
1648  for action in postConfigActions:
1649  action()
1650 
1651 
1653  """
1654  Function to select all and only the configurables that have to be used in
1655  GaudiPython.AppMgr constructor.
1656  This is needed because in Athena the implementation have to be different (the
1657  configuration is used in a different moment).
1658  """
1659  return [
1660  k for k, v in Configurable.allConfigurables.items()
1661  if v.getGaudiType() != "User"
1662  ] # Exclude ConfigurableUser instances
1663 
1664 
1665 def purge():
1666  """
1667  Clean up all configurations and configurables.
1668  """
1669  for c in Configurable.allConfigurables.values():
1670  c.__class__.configurables.clear()
1671  Configurable.allConfigurables.clear()
1672  # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
1673  # migrated to the correct class when this is known.
1674  ConfigurableGeneric.configurables.clear()
1675  from ProcessJobOptions import _included_files
1676  import os.path
1677  import sys
1678  for file in _included_files:
1679  dirname, basname = os.path.split(file)
1680  basname, ext = os.path.splitext(basname)
1681  if basname in sys.modules:
1682  del sys.modules[basname]
1683  _included_files.clear()
1684 
1685 
1687  def __init__(self):
1688  self.stack = []
1689 
1690  @property
1691  def sequence(self):
1692  return self.stack[-1]
1693 
1694  def enter(self, visitee):
1695  pass
1696 
1697  def _getUniqueName(self, prefix):
1698  from Gaudi.Configuration import allConfigurables
1699  cnt = 0
1700  name = prefix + str(cnt)
1701  while name in allConfigurables:
1702  cnt += 1
1703  name = prefix + str(cnt)
1704  return name
1705 
1706  def _newSeq(self, prefix='seq_', **kwargs):
1707  from Configurables import GaudiSequencer
1708  return GaudiSequencer(self._getUniqueName('seq_'), **kwargs)
1709 
1710  def leave(self, visitee):
1711  stack = self.stack
1712  if visitee in (CFTrue, CFFalse):
1713  stack.append(self._newSeq(Invert=visitee is CFFalse))
1714  elif isinstance(visitee, (ControlFlowLeaf, ConfigurableAlgorithm)):
1715  stack.append(visitee)
1716  elif isinstance(visitee, (OrNode, AndNode, OrderedNode)):
1717  b = stack.pop()
1718  a = stack.pop()
1719  seq = self._newSeq(
1720  Members=[a, b],
1721  ModeOR=isinstance(visitee, OrNode),
1722  ShortCircuit=not isinstance(visitee, OrderedNode),
1723  MeasureTime=True)
1724  stack.append(seq)
1725  elif isinstance(visitee, ignore):
1726  if hasattr(stack[-1], 'IgnoreFilterPassed'):
1727  stack[-1].IgnoreFilterPassed = True
1728  else:
1729  stack.append(
1730  self._newSeq(
1731  Members=[stack.pop()], IgnoreFilterPassed=True))
1732  elif isinstance(visitee, InvertNode):
1733  if hasattr(stack[-1], 'Invert'):
1734  stack[-1].Invert = True
1735  else:
1736  stack.append(self._newSeq(Members=[stack.pop()], Invert=True))
1737 
1738 
1739 def makeSequences(expression):
1740  '''
1741  Convert a control flow expression to nested GaudiSequencers.
1742  '''
1743  if not isinstance(expression, ControlFlowNode):
1744  raise ValueError('ControlFlowNode instance expected, got %s' %
1745  type(expression).__name__)
1746  visitor = CreateSequencesVisitor()
1747  expression.visitNode(visitor)
1748  return visitor.sequence
1749 
1750 
1751 class SuperAlgorithm(ControlFlowNode):
1752  '''
1753  Helper class to use a ControlFlowNode as an algorithm configurable
1754  instance.
1755  '''
1756 
1757  def __new__(cls, name=None, **kwargs):
1758  if name is None:
1759  name = cls.__name__
1760  if name in Configurable.allConfigurables:
1761  instance = Configurable.allConfigurables[name]
1762  assert type(instance) is cls, \
1763  ('trying to reuse {0!r} as name of a {1} instance while it''s '
1764  'already used for an instance of {2}').format(
1765  name,
1766  cls.__name__,
1767  type(instance).__name__)
1768  return instance
1769  else:
1770  instance = super(SuperAlgorithm, cls).__new__(cls, name, **kwargs)
1771  Configurable.allConfigurables[name] = instance
1772  return instance
1773 
1774  def __init__(self, name=None, **kwargs):
1775  self._name = name or self.getType()
1776  self.graph = self._initGraph()
1777  for key in kwargs:
1778  setattr(self, key, kwargs[key])
1779 
1780  @property
1781  def name(self): # name is a read only property
1782  return self._name
1783 
1784  @classmethod
1785  def getType(cls):
1786  return cls.__name__
1787 
1788  # required to be registered in allConfigurables
1789  def properties(self):
1790  pass
1791 
1792  # required to be registered in allConfigurables
1793  def isApplicable(self):
1794  return False
1795 
1796  # required to be registered in allConfigurables
1797  @classmethod
1798  def getGaudiType(cls):
1799  return 'User'
1800 
1801  def _makeAlg(self, typ, **kwargs):
1802  '''
1803  Instantiate and algorithm of type 'typ' with a name suitable for use
1804  inside a SuperAlgorithm.
1805  '''
1806  name = '{0}_{1}'.format(self.name, kwargs.pop('name', typ.getType()))
1807  return typ(name, **kwargs)
1808 
1809  def _initGraph(self):
1810  raise NotImplementedError()
1811 
1812  def __repr__(self):
1813  return '{0}({1!r})'.format(self.getType(), self.name)
1814 
1815  def _visitSubNodes(self, visitor):
1816  if self.graph:
1817  self.graph.visitNode(visitor)
1818 
1819  def __setattr__(self, name, value):
1820  super(SuperAlgorithm, self).__setattr__(name, value)
1821  if name in ('_name', 'graph'):
1822  # do not propagate internal data members
1823  return
1824 
1825  class PropSetter(object):
1826  def enter(self, node):
1827  try:
1828  setattr(node, name, value)
1829  except (ValueError, AttributeError):
1830  # ignore type and name mismatch
1831  pass
1832 
1833  def leave(self, node):
1834  pass
1835 
1836  self._visitSubNodes(PropSetter())
def __init__(self, name=None, **kwargs)
def propagateProperty(self, name, others=None, force=True)
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:109
def _printHeader(indentStr, title)
def __new__(cls, name=None, **kwargs)
def clone(self, name=None, **kwargs)
def appendPostConfigAction(function)
def __new__(cls, *args, **kwargs)
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)
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, _enabled=True, **kwargs)
def __init__(self, name=Configurable.DefaultName)
def __init__(self, name=Configurable.DefaultName)
def copyChildAndSetParent(self, cfg, parent)
def __iadd__(self, configs, descr=None)
auto get(const Handle &handle, const Algo &, const EventContext &) -> decltype(details::deref(handle.get()))
def removePostConfigAction(function)
def __str__(self, indent=0, headerLastIndentUnit=indentUnit)
def __init__(self, name=Configurable.DefaultName)
def _newSeq(self, prefix='seq_', **kwargs)
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...